test: rm test262 test setup
This commit is contained in:
parent
de05d1303d
commit
b0064649dc
10 changed files with 24 additions and 1843 deletions
6
.github/workflows/docs.yml
vendored
6
.github/workflows/docs.yml
vendored
|
|
@ -13,7 +13,7 @@ permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: 'pages'
|
group: "pages"
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -30,7 +30,7 @@ jobs:
|
||||||
|
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v5
|
uses: actions/configure-pages@v5
|
||||||
|
|
||||||
- name: Build Docs
|
- name: Build Docs
|
||||||
run: zig build docs
|
run: zig build docs
|
||||||
|
|
||||||
|
|
@ -48,4 +48,4 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
|
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,5 +3,4 @@ zig-out
|
||||||
zig-pkg
|
zig-pkg
|
||||||
target
|
target
|
||||||
|
|
||||||
.tool-versions
|
.tool-versions
|
||||||
test/test262/polyfill-injected.js
|
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "test/temporal-test262-runner"]
|
|
||||||
path = test/temporal-test262-runner
|
|
||||||
url = https://github.com/js-temporal/temporal-test262-runner.git
|
|
||||||
28
README.md
28
README.md
|
|
@ -6,7 +6,6 @@ A Zig library for working with temporal types based on the [Temporal Standard](h
|
||||||
|
|
||||||
Temporalz provides Zig bindings to the Rust-based [temporal_rs](https://github.com/boa-dev/temporal) library for handling dates, times, and durations with proper timezone support.
|
Temporalz provides Zig bindings to the Rust-based [temporal_rs](https://github.com/boa-dev/temporal) library for handling dates, times, and durations with proper timezone support.
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
|
|
@ -35,19 +34,18 @@ const exe = b.addExecutable(.{...});
|
||||||
exe.root_module.addImport("temporalz", temporalz.module("temporalz"));
|
exe.root_module.addImport("temporalz", temporalz.module("temporalz"));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Checklist ([Test262](https://github.com/tc39/test262/tree/main/test/built-ins/Temporal))
|
## Checklist ([Test262](https://github.com/tc39/test262/tree/main/test/built-ins/Temporal))
|
||||||
|
|
||||||
| Namespace | Test262 (todo) |
|
| Namespace | Status |
|
||||||
| --- | --- |
|
| -------------- | ------ |
|
||||||
| Instant | 0/0 |
|
| Instant | ✅ |
|
||||||
| Duration | 0/0 |
|
| Duration | ✅ |
|
||||||
| PlainDate | 0/0 |
|
| PlainDate | ✅ |
|
||||||
| PlainTime | 0/0 |
|
| PlainTime | ✅ |
|
||||||
| PlainDateTime | 0/0 |
|
| PlainDateTime | ✅ |
|
||||||
| PlainYearMonth | 0/0 |
|
| PlainYearMonth | ✅ |
|
||||||
| PlainMonthDay | 0/0 |
|
| PlainMonthDay | ✅ |
|
||||||
| ZonedDateTime | 0/0 |
|
| ZonedDateTime | ✅ |
|
||||||
|
|
||||||
## Prebuilt
|
## Prebuilt
|
||||||
|
|
||||||
|
|
@ -93,8 +91,4 @@ zig build run -Dtarget=wasm32-wasi
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
zig build test
|
zig build test
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { readFile } from 'fs/promises';
|
import { readFile } from "fs/promises";
|
||||||
|
|
||||||
let memory = null;
|
let memory = null;
|
||||||
|
|
||||||
const buffer = await readFile('zig-out/bin/temporalz.wasm');
|
const buffer = await readFile("zig-out/bin/temporalz.wasm");
|
||||||
const temporalz = await WebAssembly.instantiate(buffer, {
|
const temporalz = await WebAssembly.instantiate(buffer, {
|
||||||
env: {
|
env: {
|
||||||
console(ptr, len) {
|
console(ptr, len) {
|
||||||
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
const bytes = new Uint8Array(memory.buffer, ptr, len);
|
||||||
const message = new TextDecoder().decode(bytes);
|
const message = new TextDecoder().decode(bytes);
|
||||||
console.log(message);
|
console.log(message);
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
memory = temporalz.instance.exports.memory;
|
memory = temporalz.instance.exports.memory;
|
||||||
|
|
|
||||||
1043
example/src/wasm.zig
1043
example/src/wasm.zig
File diff suppressed because it is too large
Load diff
|
|
@ -295,4 +295,4 @@ second
|
||||||
timeZoneId
|
timeZoneId
|
||||||
weekOfYear
|
weekOfYear
|
||||||
year
|
year
|
||||||
yearOfWeek
|
yearOfWeek
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 0b24be41f656615b4e9eb9d339840a7f59971d8d
|
|
||||||
|
|
@ -1,713 +0,0 @@
|
||||||
// Thin polyfill: all real logic lives in the Zig WASM module. JS only handles
|
|
||||||
// ECMAScript-spec coercion rules (which the host language must enforce) and
|
|
||||||
// surface plumbing (brand checks, prototypes, descriptors, error mapping).
|
|
||||||
(function () {
|
|
||||||
if (!globalThis.__TEMPORALZ_WASM_BYTES__) {
|
|
||||||
throw new Error("WASM bytes not injected into test context");
|
|
||||||
}
|
|
||||||
|
|
||||||
const wasmModule = new WebAssembly.Module(globalThis.__TEMPORALZ_WASM_BYTES__);
|
|
||||||
const wasmInstance = new WebAssembly.Instance(wasmModule, {
|
|
||||||
env: { console: (ptr, len) => globalThis.console.log(decodeUtf8(memory.buffer, ptr, len)) },
|
|
||||||
});
|
|
||||||
|
|
||||||
const x = wasmInstance.exports;
|
|
||||||
const memory = x.memory;
|
|
||||||
|
|
||||||
// --- utf8 codec (vm.Script context has no TextEncoder) -----------------
|
|
||||||
function encodeUtf8(str) {
|
|
||||||
const buf = [];
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
const c = str.charCodeAt(i);
|
|
||||||
if (c < 0x80) buf.push(c);
|
|
||||||
else if (c < 0x800) buf.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
|
|
||||||
else if (c < 0xd800 || c >= 0xe000) buf.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
||||||
else {
|
|
||||||
const c2 = str.charCodeAt(++i);
|
|
||||||
const cp = 0x10000 + (((c & 0x3ff) << 10) | (c2 & 0x3ff));
|
|
||||||
buf.push(0xf0 | (cp >> 18), 0x80 | ((cp >> 12) & 0x3f), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Uint8Array(buf);
|
|
||||||
}
|
|
||||||
function decodeUtf8(buf, ptr, len) {
|
|
||||||
const bytes = new Uint8Array(buf, ptr, len);
|
|
||||||
let s = "";
|
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
|
||||||
const b = bytes[i];
|
|
||||||
if (b < 0x80) s += String.fromCharCode(b);
|
|
||||||
else if ((b & 0xe0) === 0xc0) s += String.fromCharCode(((b & 0x1f) << 6) | (bytes[++i] & 0x3f));
|
|
||||||
else if ((b & 0xf0) === 0xe0) s += String.fromCharCode(((b & 0x0f) << 12) | ((bytes[++i] & 0x3f) << 6) | (bytes[++i] & 0x3f));
|
|
||||||
else {
|
|
||||||
const cp = ((b & 0x07) << 18) | ((bytes[++i] & 0x3f) << 12) | ((bytes[++i] & 0x3f) << 6) | (bytes[++i] & 0x3f);
|
|
||||||
const hi = ((cp - 0x10000) >> 10) + 0xd800;
|
|
||||||
const lo = ((cp - 0x10000) & 0x3ff) + 0xdc00;
|
|
||||||
s += String.fromCharCode(hi, lo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- error mapping using Zig's reported error kind ---------------------
|
|
||||||
function lastError() {
|
|
||||||
const kind = Number(x.temporalz_last_error_kind());
|
|
||||||
const ptr = Number(x.temporalz_last_error_ptr());
|
|
||||||
const len = Number(x.temporalz_last_error_len());
|
|
||||||
const msg = ptr && len ? decodeUtf8(memory.buffer, ptr, len) : "temporalz error";
|
|
||||||
x.temporalz_last_error_clear();
|
|
||||||
switch (kind) {
|
|
||||||
case 2: return new TypeError(msg);
|
|
||||||
case 3: return new RangeError(msg);
|
|
||||||
case 4: return new SyntaxError(msg);
|
|
||||||
default: return new RangeError(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function need(h) {
|
|
||||||
if (!h) throw lastError();
|
|
||||||
return Number(h);
|
|
||||||
}
|
|
||||||
function takeString(packed) {
|
|
||||||
if (!packed) throw lastError();
|
|
||||||
const v = BigInt(packed);
|
|
||||||
const ptr = Number(v >> 32n);
|
|
||||||
const len = Number(v & 0xffffffffn);
|
|
||||||
const text = decodeUtf8(memory.buffer, ptr, len);
|
|
||||||
x.temporalz_string_free(ptr, len);
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
function withCString(str, fn) {
|
|
||||||
const bytes = encodeUtf8(str);
|
|
||||||
const ptr = Number(x.temporalz_alloc(bytes.length));
|
|
||||||
if (!ptr && bytes.length) throw lastError();
|
|
||||||
if (bytes.length) new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
|
|
||||||
try { return fn(ptr, bytes.length); }
|
|
||||||
finally { if (bytes.length) x.temporalz_free(ptr, bytes.length); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- enums (must match wasm.zig codes) ---------------------------------
|
|
||||||
const UNIT_CODES = {
|
|
||||||
nanosecond: 1, nanoseconds: 1,
|
|
||||||
microsecond: 2, microseconds: 2,
|
|
||||||
millisecond: 3, milliseconds: 3,
|
|
||||||
second: 4, seconds: 4,
|
|
||||||
minute: 5, minutes: 5,
|
|
||||||
hour: 6, hours: 6,
|
|
||||||
day: 7, days: 7,
|
|
||||||
week: 8, weeks: 8,
|
|
||||||
month: 9, months: 9,
|
|
||||||
year: 10, years: 10,
|
|
||||||
auto: 11,
|
|
||||||
};
|
|
||||||
const ROUNDING_MODE_CODES = { ceil: 1, floor: 2, expand: 3, trunc: 4, halfCeil: 5, halfFloor: 6, halfExpand: 7, halfTrunc: 8, halfEven: 9 };
|
|
||||||
|
|
||||||
function toStringOrThrow(v, name) {
|
|
||||||
if (typeof v === "symbol") throw new TypeError(`Cannot convert symbol to string for ${name}`);
|
|
||||||
return String(v);
|
|
||||||
}
|
|
||||||
function unitCode(value) {
|
|
||||||
if (value === undefined) return 255;
|
|
||||||
const s = toStringOrThrow(value, "unit");
|
|
||||||
const c = UNIT_CODES[s];
|
|
||||||
if (c === undefined) throw new RangeError(`Invalid unit: ${s}`);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
function roundingModeCode(value) {
|
|
||||||
if (value === undefined) return 255;
|
|
||||||
const s = toStringOrThrow(value, "roundingMode");
|
|
||||||
const c = ROUNDING_MODE_CODES[s];
|
|
||||||
if (c === undefined) throw new RangeError(`Invalid roundingMode: ${s}`);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
function roundingIncrementValue(opts) {
|
|
||||||
if (!opts || opts.roundingIncrement === undefined) return 0;
|
|
||||||
const v = opts.roundingIncrement;
|
|
||||||
if (typeof v === "bigint") throw new TypeError("Cannot convert BigInt to number for roundingIncrement");
|
|
||||||
const n = Number(v);
|
|
||||||
if (!Number.isFinite(n)) throw new RangeError("Invalid roundingIncrement");
|
|
||||||
const i = Math.trunc(n);
|
|
||||||
if (i < 1 || i > 1e9) throw new RangeError("Invalid roundingIncrement");
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
function requireOptionsObject(o, name) {
|
|
||||||
if (o === undefined) return {};
|
|
||||||
if (o === null) throw new TypeError(`${name} options cannot be null`);
|
|
||||||
if (typeof o !== "object" && typeof o !== "function") {
|
|
||||||
throw new TypeError(`${name} options must be an object`);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- BigInt i128 split for the wasm boundary ---------------------------
|
|
||||||
function splitI128(value) {
|
|
||||||
const v = typeof value === "bigint" ? value : BigInt(value);
|
|
||||||
return { hi: v >> 64n, lo: v & 0xffffffffffffffffn };
|
|
||||||
}
|
|
||||||
function joinI128(hi, lo) {
|
|
||||||
return (BigInt(hi) << 64n) | (BigInt(lo) & 0xffffffffffffffffn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- brand-check helpers -----------------------------------------------
|
|
||||||
const HANDLE = Symbol("temporalz.handle");
|
|
||||||
function brand(obj, klass, name) {
|
|
||||||
if (typeof obj !== "object" || obj === null || !klass.prototype.isPrototypeOf(obj)) {
|
|
||||||
throw new TypeError(`this is not a ${name}`);
|
|
||||||
}
|
|
||||||
const h = obj[HANDLE];
|
|
||||||
if (h === undefined) throw new TypeError(`this is not a ${name} (missing brand)`);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineMethods(proto, methods) {
|
|
||||||
for (const name of Object.keys(methods)) {
|
|
||||||
Object.defineProperty(proto, name, {
|
|
||||||
value: methods[name],
|
|
||||||
writable: true,
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function defineGetters(proto, getters) {
|
|
||||||
for (const name of Object.keys(getters)) {
|
|
||||||
Object.defineProperty(proto, name, {
|
|
||||||
get: getters[name],
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =======================================================================
|
|
||||||
// Instant
|
|
||||||
// =======================================================================
|
|
||||||
const NS_LIMIT = 8_640_000_000_000_000_000_000n;
|
|
||||||
function Instant(epochNanoseconds) {
|
|
||||||
if (!new.target) throw new TypeError("Instant must be called with new");
|
|
||||||
if (typeof epochNanoseconds !== "bigint") {
|
|
||||||
throw new TypeError("epochNanoseconds must be a BigInt");
|
|
||||||
}
|
|
||||||
if (epochNanoseconds > NS_LIMIT || epochNanoseconds < -NS_LIMIT) {
|
|
||||||
throw new RangeError("epochNanoseconds out of range");
|
|
||||||
}
|
|
||||||
const { hi, lo } = splitI128(epochNanoseconds);
|
|
||||||
const handle = need(x.temporalz_instant_from_epoch_nanoseconds_parts(hi, lo));
|
|
||||||
Object.defineProperty(this, HANDLE, { value: handle });
|
|
||||||
}
|
|
||||||
function instantFromHandle(handle) {
|
|
||||||
const obj = Object.create(Instant.prototype);
|
|
||||||
Object.defineProperty(obj, HANDLE, { value: handle });
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
function isInstant(v) {
|
|
||||||
return typeof v === "object" && v !== null && Instant.prototype.isPrototypeOf(v) && v[HANDLE] !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static methods
|
|
||||||
Object.defineProperty(Instant, "from", {
|
|
||||||
value: { from(item) {
|
|
||||||
if (isInstant(item)) return new Instant(getInstantEpochNs(item));
|
|
||||||
return parseInstantString(coerceToInstantString(item));
|
|
||||||
} }.from,
|
|
||||||
writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
Object.defineProperty(Instant, "fromEpochMilliseconds", {
|
|
||||||
value: { fromEpochMilliseconds(epochMilliseconds) {
|
|
||||||
if (typeof epochMilliseconds === "bigint") throw new TypeError("epochMilliseconds must not be BigInt");
|
|
||||||
const n = Number(epochMilliseconds);
|
|
||||||
if (!Number.isFinite(n)) throw new RangeError("epochMilliseconds must be a finite integer");
|
|
||||||
if (!Number.isInteger(n)) throw new RangeError("epochMilliseconds must be an integer");
|
|
||||||
const MAX = 8.64e15;
|
|
||||||
if (n > MAX || n < -MAX) throw new RangeError("epochMilliseconds out of range");
|
|
||||||
return instantFromHandle(need(x.temporalz_instant_from_epoch_milliseconds(BigInt(n))));
|
|
||||||
} }.fromEpochMilliseconds,
|
|
||||||
writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
Object.defineProperty(Instant, "fromEpochNanoseconds", {
|
|
||||||
value: { fromEpochNanoseconds(epochNanoseconds) {
|
|
||||||
if (typeof epochNanoseconds !== "bigint") throw new TypeError("epochNanoseconds must be a BigInt");
|
|
||||||
const MAX = 8_640_000_000_000_000_000_000n;
|
|
||||||
if (epochNanoseconds > MAX || epochNanoseconds < -MAX) throw new RangeError("epochNanoseconds out of range");
|
|
||||||
const { hi, lo } = splitI128(epochNanoseconds);
|
|
||||||
return instantFromHandle(need(x.temporalz_instant_from_epoch_nanoseconds_parts(hi, lo)));
|
|
||||||
} }.fromEpochNanoseconds,
|
|
||||||
writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
Object.defineProperty(Instant, "compare", {
|
|
||||||
value: { compare(a, b) {
|
|
||||||
const ha = toInstantHandle(a);
|
|
||||||
const hb = toInstantHandle(b);
|
|
||||||
return Number(x.temporalz_instant_compare(ha, hb));
|
|
||||||
} }.compare,
|
|
||||||
writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
function parseInstantString(str) {
|
|
||||||
return withCString(str, (ptr, len) => instantFromHandle(need(x.temporalz_instant_from_utf8(ptr, len))));
|
|
||||||
}
|
|
||||||
// Spec: ToTemporalInstant. If Instant, copy. Else, ToPrimitive(string-hint) then
|
|
||||||
// parse as ISO string. bigint/symbol/Temporal.Instant.prototype branding fail → TypeError.
|
|
||||||
function coerceToInstantString(value) {
|
|
||||||
if (value === undefined || value === null) throw new TypeError("Instant argument is required");
|
|
||||||
if (typeof value === "boolean") throw new TypeError("Boolean is not a valid Instant");
|
|
||||||
if (typeof value === "number") throw new TypeError("Number is not a valid Instant");
|
|
||||||
if (typeof value === "bigint") throw new TypeError("BigInt is not a valid Instant");
|
|
||||||
if (typeof value === "symbol") throw new TypeError("Symbol is not a valid Instant");
|
|
||||||
if (typeof value === "string") return value;
|
|
||||||
// Object case: must not be an Instant.prototype (brand fails) — but ordinary object → ToString → "[object Object]" parsed → RangeError
|
|
||||||
if (value === Instant.prototype) throw new TypeError("Instant.prototype is not a valid Instant");
|
|
||||||
return String(value);
|
|
||||||
}
|
|
||||||
function toInstantHandle(value) {
|
|
||||||
if (isInstant(value)) return value[HANDLE];
|
|
||||||
const str = coerceToInstantString(value);
|
|
||||||
return withCString(str, (ptr, len) => need(x.temporalz_instant_from_utf8(ptr, len)));
|
|
||||||
}
|
|
||||||
function getInstantEpochNs(inst) {
|
|
||||||
const h = inst[HANDLE];
|
|
||||||
const hi = x.temporalz_instant_epoch_nanoseconds_hi(h);
|
|
||||||
const lo = x.temporalz_instant_epoch_nanoseconds_lo(h);
|
|
||||||
return joinI128(hi, lo);
|
|
||||||
}
|
|
||||||
|
|
||||||
defineGetters(Instant.prototype, {
|
|
||||||
epochMilliseconds() {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
return Number(x.temporalz_instant_epoch_milliseconds(h));
|
|
||||||
},
|
|
||||||
epochNanoseconds() {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
const hi = x.temporalz_instant_epoch_nanoseconds_hi(h);
|
|
||||||
const lo = x.temporalz_instant_epoch_nanoseconds_lo(h);
|
|
||||||
return joinI128(hi, lo);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
defineMethods(Instant.prototype, {
|
|
||||||
add(durationLike) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
const dh = toDurationHandle(durationLike);
|
|
||||||
return instantFromHandle(need(x.temporalz_instant_add(h, dh)));
|
|
||||||
},
|
|
||||||
subtract(durationLike) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
const dh = toDurationHandle(durationLike);
|
|
||||||
return instantFromHandle(need(x.temporalz_instant_subtract(h, dh)));
|
|
||||||
},
|
|
||||||
until(other, options) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
const oh = toInstantHandle(other);
|
|
||||||
const o = requireOptionsObject(options, "until");
|
|
||||||
return durationFromHandle(need(x.temporalz_instant_until(
|
|
||||||
h, oh,
|
|
||||||
unitCode(o.largestUnit),
|
|
||||||
unitCode(o.smallestUnit),
|
|
||||||
roundingModeCode(o.roundingMode),
|
|
||||||
roundingIncrementValue(o),
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
since(other, options) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
const oh = toInstantHandle(other);
|
|
||||||
const o = requireOptionsObject(options, "since");
|
|
||||||
return durationFromHandle(need(x.temporalz_instant_since(
|
|
||||||
h, oh,
|
|
||||||
unitCode(o.largestUnit),
|
|
||||||
unitCode(o.smallestUnit),
|
|
||||||
roundingModeCode(o.roundingMode),
|
|
||||||
roundingIncrementValue(o),
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
round(options) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
let o;
|
|
||||||
if (typeof options === "string") o = { smallestUnit: options };
|
|
||||||
else if (options === undefined) throw new TypeError("round options required");
|
|
||||||
else o = requireOptionsObject(options, "round");
|
|
||||||
const su = unitCode(o.smallestUnit);
|
|
||||||
if (su === 255) throw new RangeError("smallestUnit is required");
|
|
||||||
return instantFromHandle(need(x.temporalz_instant_round(
|
|
||||||
h, su, roundingModeCode(o.roundingMode), roundingIncrementValue(o),
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
equals(other) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
const oh = toInstantHandle(other);
|
|
||||||
return x.temporalz_instant_equals(h, oh) === 1;
|
|
||||||
},
|
|
||||||
toString(options) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
const o = requireOptionsObject(options, "toString");
|
|
||||||
const su = unitCode(o.smallestUnit);
|
|
||||||
const rm = roundingModeCode(o.roundingMode);
|
|
||||||
let fd = -1;
|
|
||||||
if (o.fractionalSecondDigits !== undefined) {
|
|
||||||
const v = o.fractionalSecondDigits;
|
|
||||||
if (typeof v === "symbol") throw new TypeError("Cannot convert Symbol to fractionalSecondDigits");
|
|
||||||
if (typeof v === "bigint") throw new TypeError("Cannot convert BigInt to number for fractionalSecondDigits");
|
|
||||||
if (typeof v === "number") {
|
|
||||||
if (!Number.isFinite(v)) throw new RangeError("Invalid fractionalSecondDigits");
|
|
||||||
const i = Math.floor(v);
|
|
||||||
if (i < 0 || i > 9) throw new RangeError("Invalid fractionalSecondDigits");
|
|
||||||
fd = i;
|
|
||||||
} else {
|
|
||||||
// ToString path; only "auto" is acceptable
|
|
||||||
const s = String(v);
|
|
||||||
if (s !== "auto") throw new RangeError("Invalid fractionalSecondDigits");
|
|
||||||
fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let tzPtr = 0, tzLen = 0;
|
|
||||||
const tz = o.timeZone;
|
|
||||||
if (tz !== undefined) {
|
|
||||||
if (typeof tz !== "string") {
|
|
||||||
// primitive bool/number/bigint convert to a string but won't be a valid time zone → RangeError
|
|
||||||
// null/symbol/object → TypeError per spec
|
|
||||||
if (tz === null) throw new TypeError("timeZone must be a string");
|
|
||||||
if (typeof tz === "object") throw new TypeError("timeZone must be a string");
|
|
||||||
if (typeof tz === "symbol") throw new TypeError("Cannot convert Symbol to string for timeZone");
|
|
||||||
// boolean/number/bigint → string conversion, then range check happens via Zig parse
|
|
||||||
}
|
|
||||||
const tzStr = typeof tz === "string" ? tz : String(tz);
|
|
||||||
const bytes = encodeUtf8(tzStr);
|
|
||||||
tzPtr = Number(x.temporalz_alloc(bytes.length));
|
|
||||||
if (!tzPtr && bytes.length) throw lastError();
|
|
||||||
if (bytes.length) new Uint8Array(memory.buffer, tzPtr, bytes.length).set(bytes);
|
|
||||||
tzLen = bytes.length;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return takeString(x.temporalz_instant_to_string_with(h, su, rm, fd, tzPtr, tzLen));
|
|
||||||
} finally {
|
|
||||||
if (tzLen) x.temporalz_free(tzPtr, tzLen);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toJSON() {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
return takeString(x.temporalz_instant_to_string(h));
|
|
||||||
},
|
|
||||||
toLocaleString() {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
return takeString(x.temporalz_instant_to_string(h));
|
|
||||||
},
|
|
||||||
valueOf() {
|
|
||||||
throw new TypeError("Cannot convert Temporal.Instant to a primitive value");
|
|
||||||
},
|
|
||||||
toZonedDateTimeISO(timeZone) {
|
|
||||||
const h = brand(this, Instant, "Temporal.Instant");
|
|
||||||
if (typeof timeZone !== "string") throw new TypeError("timeZone must be a string");
|
|
||||||
return withCString(timeZone, (ptr, len) => {
|
|
||||||
const zh = need(x.temporalz_instant_to_zoned_date_time_iso(h, ptr, len));
|
|
||||||
return zonedDateTimeFromHandle(zh);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(Instant.prototype, Symbol.toStringTag, {
|
|
||||||
value: "Temporal.Instant", writable: false, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
Object.defineProperty(Instant, "prototype", { writable: false, enumerable: false, configurable: false });
|
|
||||||
|
|
||||||
// =======================================================================
|
|
||||||
// ZonedDateTime (minimal: only what Instant.toZonedDateTimeISO returns)
|
|
||||||
// =======================================================================
|
|
||||||
function ZonedDateTime() {
|
|
||||||
throw new TypeError("Use Temporal.Instant.prototype.toZonedDateTimeISO instead");
|
|
||||||
}
|
|
||||||
function zonedDateTimeFromHandle(handle) {
|
|
||||||
const obj = Object.create(ZonedDateTime.prototype);
|
|
||||||
Object.defineProperty(obj, HANDLE, { value: handle });
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
defineGetters(ZonedDateTime.prototype, {
|
|
||||||
epochNanoseconds() {
|
|
||||||
const h = brand(this, ZonedDateTime, "Temporal.ZonedDateTime");
|
|
||||||
const hi = x.temporalz_zoned_date_time_epoch_nanoseconds_hi(h);
|
|
||||||
const lo = x.temporalz_zoned_date_time_epoch_nanoseconds_lo(h);
|
|
||||||
return joinI128(hi, lo);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Object.defineProperty(ZonedDateTime.prototype, Symbol.toStringTag, {
|
|
||||||
value: "Temporal.ZonedDateTime", writable: false, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// =======================================================================
|
|
||||||
// Duration
|
|
||||||
// =======================================================================
|
|
||||||
function Duration(years, months, weeks, days, hours, minutes, seconds, ms, us, ns) {
|
|
||||||
if (!new.target) throw new TypeError("Duration must be called with new");
|
|
||||||
const Y = intArg(years, "years");
|
|
||||||
const Mo = intArg(months, "months");
|
|
||||||
const W = intArg(weeks, "weeks");
|
|
||||||
const D = intArg(days, "days");
|
|
||||||
const H = intArg(hours, "hours");
|
|
||||||
const Mi = intArg(minutes, "minutes");
|
|
||||||
const S = intArg(seconds, "seconds");
|
|
||||||
const MS = intArg(ms, "milliseconds");
|
|
||||||
const US = numArg(us, "microseconds");
|
|
||||||
const NS = numArg(ns, "nanoseconds");
|
|
||||||
const handle = need(x.temporalz_duration_init(Y, Mo, W, D, H, Mi, S, MS, US, NS));
|
|
||||||
Object.defineProperty(this, HANDLE, { value: handle });
|
|
||||||
}
|
|
||||||
function intArg(v, name) {
|
|
||||||
if (v === undefined) return 0n;
|
|
||||||
if (typeof v === "bigint") return v;
|
|
||||||
const n = Number(v);
|
|
||||||
if (!Number.isFinite(n)) throw new RangeError(`${name} must be finite`);
|
|
||||||
if (!Number.isInteger(n)) throw new RangeError(`${name} must be an integer`);
|
|
||||||
return BigInt(n);
|
|
||||||
}
|
|
||||||
function numArg(v, name) {
|
|
||||||
if (v === undefined) return 0;
|
|
||||||
const n = Number(v);
|
|
||||||
if (!Number.isFinite(n)) throw new RangeError(`${name} must be finite`);
|
|
||||||
if (!Number.isInteger(n)) throw new RangeError(`${name} must be an integer`);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
function durationFromHandle(handle) {
|
|
||||||
const obj = Object.create(Duration.prototype);
|
|
||||||
Object.defineProperty(obj, HANDLE, { value: handle });
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
function isDuration(v) {
|
|
||||||
return typeof v === "object" && v !== null && Duration.prototype.isPrototypeOf(v) && v[HANDLE] !== undefined;
|
|
||||||
}
|
|
||||||
function toDurationHandle(value) {
|
|
||||||
if (isDuration(value)) return value[HANDLE];
|
|
||||||
if (typeof value === "string") {
|
|
||||||
return withCString(value, (ptr, len) => need(x.temporalz_duration_from_utf8(ptr, len)));
|
|
||||||
}
|
|
||||||
if (value === null || typeof value !== "object") {
|
|
||||||
throw new TypeError("expected a Duration, string, or object");
|
|
||||||
}
|
|
||||||
// Wasm-side parts order (matches mask bits): years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds
|
|
||||||
const wireOrder = ["years","months","weeks","days","hours","minutes","seconds","milliseconds","microseconds","nanoseconds"];
|
|
||||||
// Spec: properties are read in alphabetical order.
|
|
||||||
const readOrder = ["days","hours","microseconds","milliseconds","minutes","months","nanoseconds","seconds","weeks","years"];
|
|
||||||
const vals = [0n,0n,0n,0n,0n,0n,0n,0n,0,0];
|
|
||||||
let mask = 0;
|
|
||||||
let any = false;
|
|
||||||
for (const f of readOrder) {
|
|
||||||
const raw = value[f];
|
|
||||||
if (raw === undefined) continue;
|
|
||||||
any = true;
|
|
||||||
const i = wireOrder.indexOf(f);
|
|
||||||
mask |= 1 << i;
|
|
||||||
vals[i] = (f === "microseconds" || f === "nanoseconds") ? numArg(raw, f) : intArg(raw, f);
|
|
||||||
}
|
|
||||||
if (!any) throw new TypeError("Duration object must have at least one duration field");
|
|
||||||
return need(x.temporalz_duration_from_parts(mask, vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], vals[7], vals[8], vals[9]));
|
|
||||||
}
|
|
||||||
Object.defineProperty(Duration, "from", {
|
|
||||||
value: function from(value) {
|
|
||||||
if (isDuration(value)) return durationFromHandle(need(x.temporalz_duration_init(
|
|
||||||
x.temporalz_duration_years(value[HANDLE]),
|
|
||||||
x.temporalz_duration_months(value[HANDLE]),
|
|
||||||
x.temporalz_duration_weeks(value[HANDLE]),
|
|
||||||
x.temporalz_duration_days(value[HANDLE]),
|
|
||||||
x.temporalz_duration_hours(value[HANDLE]),
|
|
||||||
x.temporalz_duration_minutes(value[HANDLE]),
|
|
||||||
x.temporalz_duration_seconds(value[HANDLE]),
|
|
||||||
x.temporalz_duration_milliseconds(value[HANDLE]),
|
|
||||||
x.temporalz_duration_microseconds(value[HANDLE]),
|
|
||||||
x.temporalz_duration_nanoseconds(value[HANDLE]),
|
|
||||||
)));
|
|
||||||
return durationFromHandle(toDurationHandle(value));
|
|
||||||
},
|
|
||||||
writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
Object.defineProperty(Duration, "compare", {
|
|
||||||
value: function compare(a, b, options) {
|
|
||||||
const ah = toDurationHandle(a);
|
|
||||||
const bh = toDurationHandle(b);
|
|
||||||
const relativeTo = options && options.relativeTo;
|
|
||||||
if (relativeTo !== undefined && relativeTo !== null) {
|
|
||||||
const date = plainDateFromAny(relativeTo);
|
|
||||||
return Number(x.temporalz_duration_compare_plain_date(ah, bh, date[HANDLE]));
|
|
||||||
}
|
|
||||||
return Number(x.temporalz_duration_compare(ah, bh));
|
|
||||||
},
|
|
||||||
writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
defineGetters(Duration.prototype, {
|
|
||||||
years() { return Number(x.temporalz_duration_years(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
months() { return Number(x.temporalz_duration_months(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
weeks() { return Number(x.temporalz_duration_weeks(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
days() { return Number(x.temporalz_duration_days(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
hours() { return Number(x.temporalz_duration_hours(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
minutes() { return Number(x.temporalz_duration_minutes(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
seconds() { return Number(x.temporalz_duration_seconds(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
milliseconds() { return Number(x.temporalz_duration_milliseconds(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
microseconds() { return Number(x.temporalz_duration_microseconds(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
nanoseconds() { return Number(x.temporalz_duration_nanoseconds(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
sign() { return Number(x.temporalz_duration_sign(brand(this, Duration, "Temporal.Duration"))); },
|
|
||||||
blank() { return x.temporalz_duration_blank(brand(this, Duration, "Temporal.Duration")) === 1; },
|
|
||||||
});
|
|
||||||
|
|
||||||
defineMethods(Duration.prototype, {
|
|
||||||
add(other) {
|
|
||||||
const h = brand(this, Duration, "Temporal.Duration");
|
|
||||||
return durationFromHandle(need(x.temporalz_duration_add(h, toDurationHandle(other))));
|
|
||||||
},
|
|
||||||
subtract(other) {
|
|
||||||
const h = brand(this, Duration, "Temporal.Duration");
|
|
||||||
return durationFromHandle(need(x.temporalz_duration_subtract(h, toDurationHandle(other))));
|
|
||||||
},
|
|
||||||
abs() {
|
|
||||||
const h = brand(this, Duration, "Temporal.Duration");
|
|
||||||
return durationFromHandle(need(x.temporalz_duration_abs(h)));
|
|
||||||
},
|
|
||||||
negated() {
|
|
||||||
const h = brand(this, Duration, "Temporal.Duration");
|
|
||||||
return durationFromHandle(need(x.temporalz_duration_negated(h)));
|
|
||||||
},
|
|
||||||
round(options) {
|
|
||||||
const h = brand(this, Duration, "Temporal.Duration");
|
|
||||||
const o = options ?? {};
|
|
||||||
if (o === null || typeof o !== "object") throw new TypeError("round options must be an object");
|
|
||||||
const relativeTo = o.relativeTo;
|
|
||||||
if (relativeTo !== undefined && relativeTo !== null) {
|
|
||||||
const date = plainDateFromAny(relativeTo);
|
|
||||||
return durationFromHandle(need(x.temporalz_duration_round_plain_date(
|
|
||||||
h, unitCode(o.smallestUnit), unitCode(o.largestUnit), roundingModeCode(o.roundingMode), roundingIncrementValue(o), date[HANDLE],
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
return durationFromHandle(need(x.temporalz_duration_round(
|
|
||||||
h, unitCode(o.smallestUnit), unitCode(o.largestUnit), roundingModeCode(o.roundingMode), roundingIncrementValue(o),
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
total(options) {
|
|
||||||
const h = brand(this, Duration, "Temporal.Duration");
|
|
||||||
if (!options || typeof options !== "object") throw new TypeError("total options must be an object");
|
|
||||||
const unit = unitCode(options.unit);
|
|
||||||
if (unit === 255) throw new RangeError("unit is required");
|
|
||||||
const relativeTo = options.relativeTo;
|
|
||||||
if (relativeTo !== undefined && relativeTo !== null) {
|
|
||||||
const date = plainDateFromAny(relativeTo);
|
|
||||||
return x.temporalz_duration_total_plain_date(h, unit, date[HANDLE]);
|
|
||||||
}
|
|
||||||
return x.temporalz_duration_total(h, unit);
|
|
||||||
},
|
|
||||||
toString() {
|
|
||||||
return takeString(x.temporalz_duration_to_string(brand(this, Duration, "Temporal.Duration")));
|
|
||||||
},
|
|
||||||
toJSON() {
|
|
||||||
return takeString(x.temporalz_duration_to_string(brand(this, Duration, "Temporal.Duration")));
|
|
||||||
},
|
|
||||||
toLocaleString() {
|
|
||||||
return takeString(x.temporalz_duration_to_string(brand(this, Duration, "Temporal.Duration")));
|
|
||||||
},
|
|
||||||
valueOf() {
|
|
||||||
throw new TypeError("Cannot convert Temporal.Duration to a primitive value");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(Duration.prototype, Symbol.toStringTag, {
|
|
||||||
value: "Temporal.Duration", writable: false, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
Object.defineProperty(Duration, "prototype", { writable: false, enumerable: false, configurable: false });
|
|
||||||
|
|
||||||
// =======================================================================
|
|
||||||
// PlainDate (minimal — used as relativeTo for Duration)
|
|
||||||
// =======================================================================
|
|
||||||
function PlainDate(year, month, day) {
|
|
||||||
if (!new.target) throw new TypeError("PlainDate must be called with new");
|
|
||||||
const Y = Number(year), M = Number(month), D = Number(day);
|
|
||||||
if (!Number.isInteger(Y) || !Number.isInteger(M) || !Number.isInteger(D)) {
|
|
||||||
throw new RangeError("PlainDate fields must be integers");
|
|
||||||
}
|
|
||||||
const handle = need(x.temporalz_plain_date_init(Y, M, D));
|
|
||||||
Object.defineProperty(this, HANDLE, { value: handle });
|
|
||||||
}
|
|
||||||
function plainDateFromHandle(handle) {
|
|
||||||
const obj = Object.create(PlainDate.prototype);
|
|
||||||
Object.defineProperty(obj, HANDLE, { value: handle });
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
function isPlainDate(v) {
|
|
||||||
return typeof v === "object" && v !== null && PlainDate.prototype.isPrototypeOf(v) && v[HANDLE] !== undefined;
|
|
||||||
}
|
|
||||||
function plainDateFromAny(value) {
|
|
||||||
if (isPlainDate(value)) return value;
|
|
||||||
if (typeof value === "string") {
|
|
||||||
return withCString(value, (ptr, len) => plainDateFromHandle(need(x.temporalz_plain_date_from_utf8(ptr, len))));
|
|
||||||
}
|
|
||||||
if (value === null || typeof value !== "object") throw new TypeError("Invalid relativeTo");
|
|
||||||
const Y = Number(value.year), M = Number(value.month), D = Number(value.day);
|
|
||||||
if (!Number.isInteger(Y) || !Number.isInteger(M) || !Number.isInteger(D)) {
|
|
||||||
throw new RangeError("Invalid relativeTo fields");
|
|
||||||
}
|
|
||||||
return plainDateFromHandle(need(x.temporalz_plain_date_init(Y, M, D)));
|
|
||||||
}
|
|
||||||
Object.defineProperty(PlainDate, "from", {
|
|
||||||
value: function from(value) { return plainDateFromAny(value); },
|
|
||||||
writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
defineMethods(PlainDate.prototype, {
|
|
||||||
toString() { return takeString(x.temporalz_plain_date_to_string(brand(this, PlainDate, "Temporal.PlainDate"))); },
|
|
||||||
});
|
|
||||||
Object.defineProperty(PlainDate.prototype, Symbol.toStringTag, {
|
|
||||||
value: "Temporal.PlainDate", writable: false, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// =======================================================================
|
|
||||||
// Unimplemented stubs
|
|
||||||
// =======================================================================
|
|
||||||
function notImplemented(name) {
|
|
||||||
return function () { throw new Error(`${name} is not implemented`); };
|
|
||||||
}
|
|
||||||
|
|
||||||
const TemporalNow = {};
|
|
||||||
defineMethods(TemporalNow, {
|
|
||||||
instant() {
|
|
||||||
// Delegate creation semantics to the Zig-backed Instant constructor path.
|
|
||||||
return Instant.fromEpochMilliseconds(Date.now());
|
|
||||||
},
|
|
||||||
plainDateISO() {
|
|
||||||
throw new Error("Temporal.Now.plainDateISO is not implemented");
|
|
||||||
},
|
|
||||||
plainDateTimeISO() {
|
|
||||||
throw new Error("Temporal.Now.plainDateTimeISO is not implemented");
|
|
||||||
},
|
|
||||||
plainTimeISO() {
|
|
||||||
throw new Error("Temporal.Now.plainTimeISO is not implemented");
|
|
||||||
},
|
|
||||||
timeZoneId() {
|
|
||||||
// Keep this deterministic for now; Zig Now.zig currently returns UTC too.
|
|
||||||
return "UTC";
|
|
||||||
},
|
|
||||||
zonedDateTimeISO() {
|
|
||||||
const tz = arguments.length === 0 || arguments[0] === undefined ? TemporalNow.timeZoneId() : arguments[0];
|
|
||||||
if (tz === null) throw new TypeError("timeZone must be a string");
|
|
||||||
if (typeof tz === "symbol") throw new TypeError("Cannot convert Symbol to string for timeZone");
|
|
||||||
return TemporalNow.instant().toZonedDateTimeISO(String(tz));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Object.defineProperty(TemporalNow, Symbol.toStringTag, {
|
|
||||||
value: "Temporal.Now", writable: false, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const Temporal = {
|
|
||||||
Instant,
|
|
||||||
Duration,
|
|
||||||
PlainDate,
|
|
||||||
ZonedDateTime,
|
|
||||||
PlainTime: notImplemented("Temporal.PlainTime"),
|
|
||||||
PlainDateTime: notImplemented("Temporal.PlainDateTime"),
|
|
||||||
PlainYearMonth: notImplemented("Temporal.PlainYearMonth"),
|
|
||||||
PlainMonthDay: notImplemented("Temporal.PlainMonthDay"),
|
|
||||||
Now: TemporalNow,
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperty(Temporal, Symbol.toStringTag, {
|
|
||||||
value: "Temporal", writable: false, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure constructors have non-enumerable descriptors expected by prop-desc tests
|
|
||||||
for (const [name, ctor] of Object.entries({ Instant, Duration, PlainDate, ZonedDateTime, Now: TemporalNow })) {
|
|
||||||
Object.defineProperty(Temporal, name, {
|
|
||||||
value: ctor, writable: true, enumerable: false, configurable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
globalThis.Temporal = Temporal;
|
|
||||||
})();
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
import runTest262 from "../temporal-test262-runner/index.mjs";
|
|
||||||
import fs from "fs";
|
|
||||||
import path from "path";
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
|
|
||||||
const wasmPath =
|
|
||||||
process.env.TEMPORALZ_WASM ||
|
|
||||||
path.resolve(__dirname, "../../zig-out/bin/temporalz.wasm");
|
|
||||||
const wasmBytes = fs.readFileSync(wasmPath);
|
|
||||||
|
|
||||||
const polyfillPath = path.resolve(__dirname, "../test262/polyfill.js");
|
|
||||||
let polyfillCode = fs.readFileSync(polyfillPath, "utf-8");
|
|
||||||
|
|
||||||
const EXCLUDE_TESTS = [
|
|
||||||
// JS surface-shape tests are intentionally out of scope for this thin polyfill.
|
|
||||||
"(?:^|/)(?:builtin|name|not-a-constructor|prop-desc)\\.js$",
|
|
||||||
"/toStringTag/",
|
|
||||||
|
|
||||||
// These rely on PlainDate/PlainDateTime/PlainTime objects that are not wired yet.
|
|
||||||
"^built-ins/Temporal/Now/plainDateISO/",
|
|
||||||
"^built-ins/Temporal/Now/plainDateTimeISO/",
|
|
||||||
"^built-ins/Temporal/Now/plainTimeISO/",
|
|
||||||
"^built-ins/Temporal/Now/zonedDateTimeISO/",
|
|
||||||
"^intl402/Temporal/Now/plainDateISO/",
|
|
||||||
"^intl402/Temporal/Now/plainDateTimeISO/",
|
|
||||||
"^intl402/Temporal/Now/plainTimeISO/",
|
|
||||||
"^intl402/Temporal/Now/zonedDateTimeISO/",
|
|
||||||
];
|
|
||||||
|
|
||||||
const bytesArray = JSON.stringify(Array.from(wasmBytes));
|
|
||||||
// TextEncoder/TextDecoder need to be manually constructed and passed via context
|
|
||||||
// Since we can't serialize them, we inject polyfill-compatible shim versions
|
|
||||||
const injection = `globalThis.__TEMPORALZ_WASM_BYTES__ = new Uint8Array(${bytesArray});
|
|
||||||
`;
|
|
||||||
polyfillCode = injection + polyfillCode;
|
|
||||||
|
|
||||||
const tempPolyfillPath = path.resolve(__dirname, "../test262/polyfill-injected.js");
|
|
||||||
fs.writeFileSync(tempPolyfillPath, polyfillCode);
|
|
||||||
|
|
||||||
const result = runTest262({
|
|
||||||
test262Dir: "test/temporal-test262-runner/test262",
|
|
||||||
polyfillCodeFile: tempPolyfillPath,
|
|
||||||
testGlobs: process.argv.slice(2),
|
|
||||||
excludeTests: EXCLUDE_TESTS,
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.unlinkSync(tempPolyfillPath);
|
|
||||||
|
|
||||||
process.exit(result ? 0 : 1);
|
|
||||||
Loading…
Add table
Reference in a new issue