@glissade/core 0.14.0-pre.0 → 0.14.0

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.
package/dist/i18n.d.ts CHANGED
@@ -26,6 +26,14 @@ type MessageTable = Record<string, string>;
26
26
  interface LocalizeOptions {
27
27
  /** the locale being resolved (carried for error context only; no behavior depends on it). */
28
28
  locale: string;
29
+ /**
30
+ * Message ids ALREADY consumed outside the doc — the free-standing `t()` ids
31
+ * resolved at module-eval / createScene() time (`getConsumedMessageIds()`).
32
+ * `localize` folds these into the "matched keys" set so the orphaned-key check
33
+ * (FIX 5) doesn't flag a legitimate `t()` key as unmatched. Omit when there is
34
+ * no ambient `t()` usage (then only node-id keys count as matched).
35
+ */
36
+ consumedIds?: ReadonlySet<string> | undefined;
29
37
  }
30
38
  declare class LocalizationError extends Error {
31
39
  constructor(message: string);
@@ -40,16 +48,30 @@ declare class LocalizationError extends Error {
40
48
  *
41
49
  * Pure: no playhead, no time, no filesystem. The base-locale table (or an empty
42
50
  * table) leaves the doc structurally identical to the input.
51
+ *
52
+ * FIX 5 (0.14 canary): a table key matching NO consumed id (neither a string-track
53
+ * node-id NOR a `t()` id passed via `opts.consumedIds`) is a stale/typo'd key that
54
+ * would silently never localize anything — so `localize` throws a `LocalizationError`
55
+ * naming every orphaned key (the inverse of `t()`'s hard-fail). A fully-matched
56
+ * table is silent.
43
57
  */
44
- declare function localize(doc: Timeline, table: MessageTable, _opts: LocalizeOptions): Timeline;
58
+ declare function localize(doc: Timeline, table: MessageTable, opts: LocalizeOptions): Timeline;
45
59
  /**
46
60
  * Install the ambient message table consulted by `t()`. Called ONCE before
47
61
  * scene construction (the render entry injects it from `--locale`). Pass
48
62
  * `undefined` to clear it (the base-locale / no-flag path leaves it unset).
63
+ * Resets the consumed-id set (a fresh table = a fresh resolution pass).
49
64
  */
50
65
  declare function setMessageTable(table: MessageTable | undefined): void;
51
66
  /** The currently installed ambient message table (undefined when none is set). */
52
67
  declare function getMessageTable(): MessageTable | undefined;
68
+ /**
69
+ * The set of ids `t()` has resolved against the ambient table since the last
70
+ * `setMessageTable`. The render entry passes this to `localize` so a key consumed
71
+ * by a free-standing `t()` (which `localize` can't see — it isn't a doc track)
72
+ * isn't reported as an orphaned table key (FIX 5).
73
+ */
74
+ declare function getConsumedMessageIds(): ReadonlySet<string>;
53
75
  /**
54
76
  * Resolve a free-standing message `id` against the ambient table — build-time
55
77
  * sugar for static Text-node text (text that is NOT animated by a doc track,
@@ -62,4 +84,4 @@ declare function getMessageTable(): MessageTable | undefined;
62
84
  */
63
85
  declare function t(id: string): string;
64
86
  //#endregion
65
- export { LocaleManifest, LocalizationError, LocalizeOptions, MessageTable, ParityError, getMessageTable, localize, requireParity, setMessageTable, t };
87
+ export { LocaleManifest, LocalizationError, LocalizeOptions, MessageTable, ParityError, getConsumedMessageIds, getMessageTable, localize, requireParity, setMessageTable, t };
package/dist/i18n.js CHANGED
@@ -55,13 +55,21 @@ function nodeIdOf(target) {
55
55
  *
56
56
  * Pure: no playhead, no time, no filesystem. The base-locale table (or an empty
57
57
  * table) leaves the doc structurally identical to the input.
58
+ *
59
+ * FIX 5 (0.14 canary): a table key matching NO consumed id (neither a string-track
60
+ * node-id NOR a `t()` id passed via `opts.consumedIds`) is a stale/typo'd key that
61
+ * would silently never localize anything — so `localize` throws a `LocalizationError`
62
+ * naming every orphaned key (the inverse of `t()`'s hard-fail). A fully-matched
63
+ * table is silent.
58
64
  */
59
- function localize(doc, table, _opts) {
65
+ function localize(doc, table, opts) {
60
66
  let changed = false;
67
+ const consumed = /* @__PURE__ */ new Set();
61
68
  const tracks = doc.tracks.map((tr) => {
62
69
  if (tr.type !== "string") return tr;
63
70
  const id = nodeIdOf(tr.target);
64
71
  if (!(id in table)) return tr;
72
+ consumed.add(id);
65
73
  const localized = table[id];
66
74
  changed = true;
67
75
  return {
@@ -72,6 +80,8 @@ function localize(doc, table, _opts) {
72
80
  })
73
81
  };
74
82
  });
83
+ const orphans = Object.keys(table).filter((key) => !consumed.has(key) && !(opts.consumedIds?.has(key) ?? false)).sort();
84
+ if (orphans.length > 0) throw new LocalizationError(`message table for locale '${opts.locale}' has ${orphans.length} key(s) that match no node-id and no t() id: ${orphans.map((k) => `'${k}'`).join(", ")} — a stale/typo'd key would silently ship base text. Remove the key, or fix it to match a target id.`);
75
85
  if (!changed) return { ...doc };
76
86
  return {
77
87
  ...doc,
@@ -79,19 +89,31 @@ function localize(doc, table, _opts) {
79
89
  };
80
90
  }
81
91
  let ambientTable;
92
+ let consumedIds = /* @__PURE__ */ new Set();
82
93
  /**
83
94
  * Install the ambient message table consulted by `t()`. Called ONCE before
84
95
  * scene construction (the render entry injects it from `--locale`). Pass
85
96
  * `undefined` to clear it (the base-locale / no-flag path leaves it unset).
97
+ * Resets the consumed-id set (a fresh table = a fresh resolution pass).
86
98
  */
87
99
  function setMessageTable(table) {
88
100
  ambientTable = table;
101
+ consumedIds = /* @__PURE__ */ new Set();
89
102
  }
90
103
  /** The currently installed ambient message table (undefined when none is set). */
91
104
  function getMessageTable() {
92
105
  return ambientTable;
93
106
  }
94
107
  /**
108
+ * The set of ids `t()` has resolved against the ambient table since the last
109
+ * `setMessageTable`. The render entry passes this to `localize` so a key consumed
110
+ * by a free-standing `t()` (which `localize` can't see — it isn't a doc track)
111
+ * isn't reported as an orphaned table key (FIX 5).
112
+ */
113
+ function getConsumedMessageIds() {
114
+ return consumedIds;
115
+ }
116
+ /**
95
117
  * Resolve a free-standing message `id` against the ambient table — build-time
96
118
  * sugar for static Text-node text (text that is NOT animated by a doc track,
97
119
  * so `localize()` can't reach it): `new Text({ text: t('hero.title') })`.
@@ -104,8 +126,9 @@ function getMessageTable() {
104
126
  function t(id) {
105
127
  if (ambientTable === void 0) return id;
106
128
  const value = ambientTable[id];
129
+ if (value !== void 0) consumedIds.add(id);
107
130
  if (value === void 0) throw new LocalizationError(`t('${id}'): no message for id '${id}' in the active message table (have: ${Object.keys(ambientTable).map((k) => `'${k}'`).join(", ") || "<empty>"})`);
108
131
  return value;
109
132
  }
110
133
  //#endregion
111
- export { LocalizationError, ParityError, getMessageTable, localize, requireParity, setMessageTable, t };
134
+ export { LocalizationError, ParityError, getConsumedMessageIds, getMessageTable, localize, requireParity, setMessageTable, t };
package/dist/index.d.ts CHANGED
@@ -312,9 +312,12 @@ interface BindTarget {
312
312
  /**
313
313
  * The value type this target accepts; a track of any other type is a bind
314
314
  * error. An ARRAY for a polymorphic prop that admits more than one type
315
- * (e.g. a Shape `fill` accepts both `color` and `paint`).
315
+ * (e.g. a Shape `fill` accepts both `color` and `paint`, or a vec2 prop that
316
+ * accepts both `vec2` and `vec2-arc`). UNDEFINED means the target opted OUT
317
+ * of the guard (an untagged custom-node prop — back-compat with 0.13, which
318
+ * had no guard): any track binds without a type check.
316
319
  */
317
- readonly expects: ValueTypeId | readonly ValueTypeId[];
320
+ readonly expects: ValueTypeId | readonly ValueTypeId[] | undefined;
318
321
  }
319
322
  /** Analytic value/velocity access to one bound target (v2 addendum §B.6). */
320
323
  interface CurveSampler {
package/dist/index.js CHANGED
@@ -854,7 +854,9 @@ function bindTimeline(compiled, resolve, playhead = createPlayhead()) {
854
854
  if (!sig) throw new UnboundTargetError(target);
855
855
  const got = tr.type;
856
856
  const expects = sig.expects;
857
- if (!(Array.isArray(expects) ? expects.includes(got) : got === expects)) throw new BindTypeMismatchError(target, got, expects);
857
+ if (expects !== void 0) {
858
+ if (!(Array.isArray(expects) ? expects.includes(got) : got === expects)) throw new BindTypeMismatchError(target, got, expects);
859
+ }
858
860
  sig.bindSource(() => sampleTrack(tr, playhead()));
859
861
  bound.push(sig);
860
862
  samplers.set(target, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/core",
3
- "version": "0.14.0-pre.0",
3
+ "version": "0.14.0",
4
4
  "description": "glissade core: signals, tracks, timeline document, evaluation, easing, springs, seeded RNG. Zero DOM/Node dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {