teptris 0.3.23 → 0.3.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +90 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teptris",
3
- "version": "0.3.23",
3
+ "version": "0.3.24",
4
4
  "description": "TOML for JavaScript/TypeScript at libteptris speed (WebAssembly, no fallback) - version tracks the engine ({engine}.{iteration}; npm semver is 3-part)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -3,6 +3,19 @@
3
3
 
4
4
  const KIND_OFFSET = 4, KIND_LOCAL_DT = 5, KIND_DATE = 6, KIND_TIME = 7;
5
5
 
6
+ // Date instances are the JS ecosystem's datetime shape (smol-toml
7
+ // hands them back) - they re-enter as the engine's datetime op. The
8
+ // fields come straight off the Date (toISOString + the parse regex
9
+ // per value measured as the datetime-heavy hot cost); Date carries
10
+ // millisecond precision, so nanoseconds = ms * 1e6.
11
+ const dateToDt = (d) => ({
12
+ kind: KIND_OFFSET, /* toISOString always yields Z */
13
+ year: d.getUTCFullYear(), month: d.getUTCMonth() + 1,
14
+ day: d.getUTCDate(), hour: d.getUTCHours(),
15
+ minute: d.getUTCMinutes(), second: d.getUTCSeconds(),
16
+ nanosecond: d.getUTCMilliseconds() * 1e6, offset_seconds: 0,
17
+ });
18
+
6
19
  function cstr(mod, s) {
7
20
  const bytes = mod.lengthBytesUTF8(s) + 1;
8
21
  const ptr = mod._teptris_wasm_alloc(bytes);
@@ -131,6 +144,15 @@ function buildValue(mod, b, key, v, errs) {
131
144
  default:
132
145
  break;
133
146
  }
147
+ if (v instanceof Date) {
148
+ // the JS ecosystem's datetime shape re-enters as the engine's
149
+ // datetime op (same mapping as the RFC 3339 string form)
150
+ const dt = dateToDt(v);
151
+ mod._teptris_wasm_builder_put_datetime(
152
+ b, kp, dt.kind, dt.year, dt.month, dt.day, dt.hour,
153
+ dt.minute, dt.second, dt.nanosecond, dt.offset_seconds);
154
+ return;
155
+ }
134
156
  if (Array.isArray(v)) {
135
157
  mod._teptris_wasm_builder_open_array(b, kp);
136
158
  for (const item of v) buildValue(mod, b, null, item, errs);
@@ -278,9 +300,73 @@ export function initApi(mod) {
278
300
  n += 1 + written;
279
301
  }
280
302
  };
303
+ // the datetime record is value-only (the key op precedes it)
304
+ const writeDatetime = (k, dt) => {
305
+ if (k !== null) wkey(k);
306
+ ensure(27);
307
+ buf[n++] = 7;
308
+ buf[n++] = dt.kind;
309
+ i64(dt.year);
310
+ buf[n++] = dt.month; buf[n++] = dt.day; buf[n++] = dt.hour;
311
+ buf[n++] = dt.minute; buf[n++] = dt.second;
312
+ u32(dt.nanosecond);
313
+ i64(dt.offset_seconds);
314
+ };
281
315
  const scalar = (k, v) => {
282
316
  // array ELEMENTS carry no key op (writer grammar v0.3.11): the
283
317
  // old klen-0 marker is now a genuine empty key
318
+ if (v instanceof Date) {
319
+ writeDatetime(k, dateToDt(v));
320
+ return;
321
+ }
322
+ // HOT PATH: keyed int/bool scalars (the mega-flat shapes are
323
+ // exactly this) - ONE growth check for key + value together,
324
+ // wkey inlined, direct DataView writes. The profiled dump cost
325
+ // was 91% JS walk: per-byte closure calls dominated.
326
+ if (k !== null && typeof v === "number" && !Object.is(v, -0) &&
327
+ Number.isInteger(v) && v >= -I64_BOUND && v < I64_BOUND) {
328
+ ensure(4 + k.length * 3 + 8);
329
+ buf[n++] = 1; /* the KEY op */
330
+ let ascii = true;
331
+ for (let i = 0; i < k.length; i++) {
332
+ const c = k.charCodeAt(i);
333
+ if (c > 127) { ascii = false; break; }
334
+ buf[n + 1 + i] = c;
335
+ }
336
+ if (ascii) {
337
+ buf[n] = k.length; n += 1 + k.length;
338
+ } else {
339
+ const { written } = utf8enc.encodeInto(k, buf.subarray(n + 1));
340
+ buf[n] = written; n += 1 + written;
341
+ }
342
+ buf[n++] = 3;
343
+ if (v >= -2147483648 && v <= 2147483647) {
344
+ dv.setInt32(n, v, true);
345
+ dv.setInt32(n + 4, v < 0 ? -1 : 0, true);
346
+ } else {
347
+ dv.setBigInt64(n, BigInt(v), true);
348
+ }
349
+ n += 8;
350
+ return;
351
+ }
352
+ if (k !== null && typeof v === "boolean") {
353
+ ensure(3 + k.length * 3 + 1);
354
+ buf[n++] = 1; /* the KEY op */
355
+ let ascii = true;
356
+ for (let i = 0; i < k.length; i++) {
357
+ const c = k.charCodeAt(i);
358
+ if (c > 127) { ascii = false; break; }
359
+ buf[n + 1 + i] = c;
360
+ }
361
+ if (ascii) {
362
+ buf[n] = k.length; n += 1 + k.length;
363
+ } else {
364
+ const { written } = utf8enc.encodeInto(k, buf.subarray(n + 1));
365
+ buf[n] = written; n += 1 + written;
366
+ }
367
+ buf[n++] = v ? 6 : 5;
368
+ return;
369
+ }
284
370
  if (k !== null) wkey(k);
285
371
  if (typeof v === "boolean") { byte(v ? 6 : 5); return; }
286
372
  if (typeof v === "number") {
@@ -304,18 +390,7 @@ export function initApi(mod) {
304
390
  }
305
391
  if (typeof v === "string") {
306
392
  if (maybeRfc3339(v) && RFC3339.test(v)) {
307
- const dt = parseDateTime(v);
308
- // the key prefix is already written (top of scalar()); the
309
- // datetime record is value-only - re-sending the key here
310
- // crashed bare array elements (k === null)
311
- ensure(27);
312
- buf[n++] = 7;
313
- buf[n++] = dt.kind;
314
- i64(dt.year);
315
- buf[n++] = dt.month; buf[n++] = dt.day; buf[n++] = dt.hour;
316
- buf[n++] = dt.minute; buf[n++] = dt.second;
317
- u32(dt.nanosecond);
318
- i64(dt.offset_seconds);
393
+ writeDatetime(null, parseDateTime(v));
319
394
  return;
320
395
  }
321
396
  ensure(5 + v.length * 3);
@@ -341,7 +416,8 @@ export function initApi(mod) {
341
416
  const isScalar = (v) =>
342
417
  v !== null && (typeof v !== "object" || Array.isArray(v) === false &&
343
418
  (typeof v === "string" || typeof v === "number" ||
344
- typeof v === "boolean" || typeof v === "bigint"));
419
+ typeof v === "boolean" || typeof v === "bigint")) ||
420
+ v instanceof Date;
345
421
  let depth = 0;
346
422
  const members = (o, inline) => {
347
423
  // name the engine's 512 ceiling: without this the JS stack
@@ -365,7 +441,7 @@ export function initApi(mod) {
365
441
  if (Array.isArray(v)) {
366
442
  const aot = v.length > 0 && !inline && v.every(
367
443
  (x) => x !== null && typeof x === "object" &&
368
- !Array.isArray(x));
444
+ !Array.isArray(x) && !(x instanceof Date));
369
445
  if (aot) {
370
446
  // element bodies are EXPLICIT (the element opens with the
371
447
  // section op): nested tables promote to [path.sub]