@wcstack/state 3.2.0 → 3.4.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.
Files changed (57) hide show
  1. package/README.ja.md +140 -83
  2. package/README.md +141 -84
  3. package/dist/auto.min.js +1 -1
  4. package/dist/auto.min.js.map +1 -1
  5. package/dist/index.d.ts +74 -8
  6. package/dist/index.esm.js +1 -1
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/manifest.d.ts +28 -5
  9. package/dist/manifest.esm.js +253 -246
  10. package/dist/parser.d.ts +16 -2
  11. package/dist/parser.esm.js +223 -50
  12. package/dist/split/chunks/binder.js +1 -1
  13. package/dist/split/chunks/binder.js.map +1 -1
  14. package/dist/split/chunks/checkDependency.js +1 -1
  15. package/dist/split/chunks/checkDependency.js.map +1 -1
  16. package/dist/split/chunks/declarationAliases.js +1 -1
  17. package/dist/split/chunks/declarationAliases.js.map +1 -1
  18. package/dist/split/chunks/declarationHooks.js +2 -0
  19. package/dist/split/chunks/declarationHooks.js.map +1 -0
  20. package/dist/split/chunks/diagnosticsHooks.js.map +1 -1
  21. package/dist/split/chunks/featureBridge.js +2 -0
  22. package/dist/split/chunks/featureBridge.js.map +1 -0
  23. package/dist/split/chunks/filterRegistry.js +1 -1
  24. package/dist/split/chunks/filterRegistry.js.map +1 -1
  25. package/dist/split/chunks/getAllPropertyDescriptors.js +1 -1
  26. package/dist/split/chunks/getAllPropertyDescriptors.js.map +1 -1
  27. package/dist/split/chunks/lifecycleHooks.js +1 -1
  28. package/dist/split/chunks/pathDiagnostics.js +1 -1
  29. package/dist/split/chunks/pathDiagnostics.js.map +1 -1
  30. package/dist/split/chunks/registerComponents.js +1 -1
  31. package/dist/split/chunks/registerComponents.js.map +1 -1
  32. package/dist/split/chunks/setAll.js +1 -1
  33. package/dist/split/chunks/setAll.js.map +1 -1
  34. package/dist/split/chunks/version.js +1 -1
  35. package/dist/split/core.d.ts +64 -2
  36. package/dist/split/core.js +1 -1
  37. package/dist/split/core.js.map +1 -1
  38. package/dist/split/features/devtools.js +1 -1
  39. package/dist/split/features/devtools.js.map +1 -1
  40. package/dist/split/features/diagnostics.js +1 -1
  41. package/dist/split/features/diagnostics.js.map +1 -1
  42. package/dist/split/features/formats.js +1 -1
  43. package/dist/split/features/formats.js.map +1 -1
  44. package/dist/split/features/recursion.js +1 -1
  45. package/dist/split/features/recursion.js.map +1 -1
  46. package/dist/split/features/scopes.js +1 -1
  47. package/dist/split/features/scopes.js.map +1 -1
  48. package/dist/split/features/ssr.js +1 -1
  49. package/dist/split/features/ssr.js.map +1 -1
  50. package/dist/split/features/temporal.js +1 -1
  51. package/dist/split/features/temporal.js.map +1 -1
  52. package/dist/wcs-manifest.json +36 -21
  53. package/package.json +1 -1
  54. package/dist/split/chunks/mount.js +0 -2
  55. package/dist/split/chunks/mount.js.map +0 -1
  56. package/dist/split/chunks/watchRuntime.js +0 -2
  57. package/dist/split/chunks/watchRuntime.js.map +0 -1
@@ -6,10 +6,99 @@ const _config = {
6
6
  // backward compatible export (read-only usage)
7
7
  const config = _config;
8
8
 
9
+ const DELIMITER = '.';
10
+ const WILDCARD = '*';
11
+ const MAX_WILDCARD_DEPTH = 128;
12
+ // data-wcs バインディング構文 `[prop][#mod]: [path][|filter...]` の区切り文字(単一正本・`@state` は v2 で撤去)。
13
+ // これらは「死守の壁(構文契約)」であり値は不変。manifest.syntax.delimiters で公開される。
14
+ const BINDING_SEPARATOR = ';'; // 複数バインディングの区切り
15
+ const PROP_VALUE_SEPARATOR = ':'; // 左辺(prop)と右辺(path)の区切り
16
+ const MODIFIER_SEPARATOR = '#'; // prop と修飾子の区切り
17
+ const FILTER_SEPARATOR = '|'; // フィルタパイプの区切り
18
+ // 修飾子(`#` 後)の語彙(単一正本)。manifest.syntax.modifiers で公開される。
19
+ // フラグ形(`#prevent` — 値を取らない)とキー値形(`#init=element` — `=` で値を取る)。
20
+ // 消費箇所(event/handler・BindingSession・twowayHandler・bindings/initialSync)は
21
+ // この定数を参照する — 文字列リテラルの散在は tooling への収載漏れの温床だった
22
+ // (docs/static-wiring-dx-design.md §2-2)。
23
+ const MODIFIER_PREVENT = 'prevent';
24
+ const MODIFIER_STOP = 'stop';
25
+ const MODIFIER_READONLY = 'ro';
26
+ const MODIFIER_FLAGS = Object.freeze([
27
+ MODIFIER_PREVENT, MODIFIER_STOP, MODIFIER_READONLY,
28
+ ]);
29
+ const MODIFIER_KEY_INIT = 'init';
30
+ const MODIFIER_KEY_SYNC = 'sync';
31
+ const MODIFIER_KEYS = Object.freeze([
32
+ MODIFIER_KEY_INIT, MODIFIER_KEY_SYNC,
33
+ ]);
34
+ // bindingType 判別と左辺 namespace の語彙(単一正本)。manifest.syntax.bindingTypes で
35
+ // 公開される。パーサ(parseBindTextsForElement)とイベント層はこの定数に分岐する。
36
+ // apply 層のディスパッチマップ(apply/applyChange.ts の applyChangeByFirstSegment)の
37
+ // キー集合との一致は __tests__/manifest.test.ts の drift テストが強制する —
38
+ // manifest エントリ(DOM 非依存)から apply 層を import しないための分離。
39
+ const ELSE_KEYWORD = 'else';
40
+ const SPREAD_PROP = '...';
41
+ const EVENT_PROP_PREFIX = 'on';
42
+ const EVENT_TOKEN_NAMESPACE = 'eventToken';
43
+ const COMMAND_NAMESPACE = 'command';
44
+ const CLASS_NAMESPACE = 'class';
45
+ const ATTR_NAMESPACE = 'attr';
46
+ const STYLE_NAMESPACE = 'style';
47
+ // リストインデックス参照名(`$1`..`$N`)の接頭辞(単一正本)。
48
+ // manifest.syntax.indexParam で公開される。
49
+ const INDEX_PARAM_PREFIX = '$';
50
+ /**
51
+ * stackIndexByIndexName
52
+ * インデックス名からスタックインデックスへのマッピング
53
+ * $1 => 0
54
+ * $2 => 1
55
+ * :
56
+ * ${i + 1} => i
57
+ * i < MAX_WILDCARD_DEPTH
58
+ */
59
+ const tmpIndexByIndexName = {};
60
+ for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
61
+ tmpIndexByIndexName[`${INDEX_PARAM_PREFIX}${i + 1}`] = i;
62
+ }
63
+ Object.freeze(tmpIndexByIndexName);
64
+ const STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
65
+ const STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
66
+ /** 旧名 `$updatedCallback` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
67
+ const STATE_RENDERED_CALLBACK_NAME = "$renderedCallback";
68
+ const STATE_ERROR_CALLBACK_NAME = "$errorCallback";
69
+ const WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
70
+ const STATE_BINDABLES_NAME = "$bindables";
71
+ const STATE_COMMANDS_NAME = "$commands";
72
+ const STATE_COMMAND_TOKENS_NAME = "$commandTokens";
73
+ const STATE_COMMAND_NAMESPACE_NAME = "$command";
74
+ const STATE_EVENT_TOKENS_NAME = "$eventTokens";
75
+ const STATE_ON_NAME = "$on";
76
+ /** 旧名 `$streams` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
77
+ const STATE_STREAM_NAME = "$stream";
78
+ const STATE_WATCH_NAME = "$watch";
79
+ const STATE_SCAN_NAME = "$scan";
80
+ const STATE_RECURSION_NAME = "$recursion";
81
+ const STATE_LIST_KEYS_NAME = "$listKeys";
82
+ const STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
83
+ const STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
84
+
9
85
  function raiseError(message) {
10
86
  throw new Error(`[@wcstack/state] ${message}`);
11
87
  }
12
88
 
89
+ /**
90
+ * declarationAliases.ts — 宣言キーの旧名 → 正式名(要件 B12・docs/state-3x-naming.ja.md V13 / V14)。
91
+ *
92
+ * `$updatedCallback` → `$renderedCallback`、`$streams` → `$stream`。旧名は 3.x の間は受け、4.0 で外す(D4)。
93
+ * state オブジェクトがランタイムに入る入口(State の `_state` と `_loadStateFromSource`、マウント記録)で
94
+ * 1 回だけ正規化し、以降のすべての読み手は正式名だけを見る。両方の綴りを宣言したらどちらが効くのか
95
+ * 書き手に見えないので、名指しで落とす(D38)。
96
+ */
97
+ const DECLARATION_ALIASES = {
98
+ $updatedCallback: STATE_RENDERED_CALLBACK_NAME,
99
+ $streams: STATE_STREAM_NAME,
100
+ };
101
+
13
102
  /**
14
103
  * errorMessages.ts
15
104
  *
@@ -23,8 +112,8 @@ function raiseError(message) {
23
112
  * - optionsRequired: Error when required option is not specified
24
113
  * - optionMustBeNumber: Error when option value is not a number
25
114
  * - valueMustBeNumber: Error when value is not a number
26
- * - valueMustBeBoolean: Error when value is not boolean
27
115
  * - valueMustBeDate: Error when value is not a Date
116
+ * - valueMustBeArray: Error when value is not an array
28
117
  */
29
118
  /**
30
119
  * Throws error when filter requires at least one option but none provided.
@@ -53,15 +142,6 @@ function optionMustBeNumber(fnName) {
53
142
  function valueMustBeNumber(fnName) {
54
143
  raiseError(`filter ${fnName} requires a number value`);
55
144
  }
56
- /**
57
- * Throws error when filter requires boolean value but non-boolean provided.
58
- *
59
- * @param fnName - Name of the filter function
60
- * @returns Never returns (always throws)
61
- */
62
- function valueMustBeBoolean(fnName) {
63
- raiseError(`filter ${fnName} requires a boolean value`);
64
- }
65
145
  /**
66
146
  * Throws error when filter requires Date value but non-Date provided.
67
147
  *
@@ -123,6 +203,47 @@ function validateNumberString(value) {
123
203
  }
124
204
  return true;
125
205
  }
206
+ /**
207
+ * Reads one numeric option **at construction time** and returns the number itself.
208
+ *
209
+ * Every numeric filter used to write out the same three lines (`options?.[i] ?? optionsRequired`,
210
+ * `validateNumberString`, `optionMustBeNumber`) and then call `Number(opt)` again on **every
211
+ * apply**. Folding it here keeps the option checks in one place and takes the conversion off the
212
+ * hot path.
213
+ */
214
+ function numberOption(value, fnName) {
215
+ if (!validateNumberString(value)) {
216
+ optionMustBeNumber(fnName);
217
+ }
218
+ return Number(value);
219
+ }
220
+ /** A required numeric option: missing → `optionsRequired`, non-numeric → `optionMustBeNumber`. */
221
+ function requiredNumberOption(options, index, fnName) {
222
+ return numberOption(options?.[index] ?? optionsRequired(fnName), fnName);
223
+ }
224
+ /**
225
+ * Extends the display-side empty-value contract (requirement B8) to the formatting filters.
226
+ *
227
+ * A filter that builds its result with `String(value)` turns `undefined` / `null` into the
228
+ * *characters* `"undefined"` / `"null"`, so `attr.title: x|trim` wrote `title="undefined"` where
229
+ * the unfiltered `attr.title: x` correctly removes the attribute — one filter was enough to undo
230
+ * what B8 established. This family therefore passes an absent value straight through and leaves
231
+ * the decision to the apply side (attribute removed, class cleared, style cleared, text emptied).
232
+ *
233
+ * Deliberately **not** wrapped:
234
+ * - filters for which an absent value *is* the input: `defaults` / `coalesce` / `nullIfEmpty` /
235
+ * `boolean` / `truthy` / `falsy` / `not` / `eq` / `ne`;
236
+ * - the conversions, whose whole job is to convert: `int` / `float` / `number` / `string`;
237
+ * - the filters that reject a value of the wrong type (the number, date and array families).
238
+ * Their throw is confined to the one binding and reported through `$errorCallback`
239
+ * (`apply/applyChangeFromBindings.ts`), which is a different quality of failure from silently
240
+ * painting the word "undefined" into the page.
241
+ */
242
+ const nullishPassthrough = (factory) => (options) => {
243
+ const filterFn = factory(options);
244
+ // `== null` は null と undefined の両方(意図的な緩い比較)
245
+ return (value) => (value == null ? value : filterFn(value));
246
+ };
126
247
  /**
127
248
  * Equality filter - compares value with option.
128
249
  *
@@ -136,6 +257,16 @@ const eq = (options, literals) => {
136
257
  return (value) => {
137
258
  // Align types for comparison
138
259
  if (typeof value === 'number') {
260
+ // A typed literal that is not a string compares as itself (B9). A number is never equal to
261
+ // `true` / `false` / `null`, so `selectedId|eq(null)` is simply false once the id is a
262
+ // number — it used to throw `optionMustBeNumber` on **every apply**, which broke every
263
+ // binding on a path that is sometimes null and sometimes numeric. Only an unquoted
264
+ // non-number (a bare string such as `eq(abc)`) still reports the type mismatch, and it has
265
+ // to stay inside the closure: `eq` accepts values of any type, so `status|eq(active)` is
266
+ // perfectly valid and cannot be rejected at construction the way `lt` / `add` can.
267
+ if (typeof literal !== 'string') {
268
+ return value === literal;
269
+ }
139
270
  if (!validateNumberString(opt)) {
140
271
  optionMustBeNumber('eq');
141
272
  }
@@ -160,6 +291,11 @@ const ne = (options, literals) => {
160
291
  return (value) => {
161
292
  // Align types for comparison
162
293
  if (typeof value === 'number') {
294
+ // Same as `eq`: a typed `true` / `false` / `null` compares as itself instead of being
295
+ // forced through `Number()` (B9)
296
+ if (typeof literal !== 'string') {
297
+ return value !== literal;
298
+ }
163
299
  if (!validateNumberString(opt)) {
164
300
  optionMustBeNumber('ne');
165
301
  }
@@ -173,18 +309,22 @@ const ne = (options, literals) => {
173
309
  };
174
310
  };
175
311
  /**
176
- * Boolean NOT filter - inverts boolean value.
312
+ * Boolean NOT filter - inverts the truthiness of the value.
313
+ *
314
+ * Deliberately the same lenient `!value` as the engine-owned copy in
315
+ * `core/filterRegistry.ts` (`CORE_FILTERS.not`). `if:` / `else:` are built as one
316
+ * `if` parse result plus an engine-injected `not` (`structural/createNotFilter.ts`),
317
+ * and `apply/applyChangeToIf.ts` coerces the `if` side with `Boolean()`. A strict
318
+ * `not` therefore made `else:` throw — and render neither branch — whenever the
319
+ * condition was a falsy non-boolean (`0` / `""` / `undefined` / `null`). Keeping the
320
+ * two implementations identical also means a page behaves the same whether or not
321
+ * `features/formats` is installed (the split core entry registers no `not`).
177
322
  *
178
323
  * @param options - Unused
179
- * @returns Filter function that returns inverted boolean
324
+ * @returns Filter function that returns the inverted truthiness
180
325
  */
181
326
  const not = (_options) => {
182
- return (value) => {
183
- if (typeof value !== 'boolean') {
184
- valueMustBeBoolean('not');
185
- }
186
- return !value;
187
- };
327
+ return (value) => !value;
188
328
  };
189
329
  /**
190
330
  * Less than filter - checks if value is less than option.
@@ -193,15 +333,12 @@ const not = (_options) => {
193
333
  * @returns Filter function that returns boolean
194
334
  */
195
335
  const lt = (options) => {
196
- const opt = options?.[0] ?? optionsRequired('lt');
197
- if (!validateNumberString(opt)) {
198
- optionMustBeNumber('lt');
199
- }
336
+ const opt = requiredNumberOption(options, 0, 'lt');
200
337
  return (value) => {
201
338
  if (typeof value !== 'number') {
202
339
  valueMustBeNumber('lt');
203
340
  }
204
- return value < Number(opt);
341
+ return value < opt;
205
342
  };
206
343
  };
207
344
  /**
@@ -211,15 +348,12 @@ const lt = (options) => {
211
348
  * @returns Filter function that returns boolean
212
349
  */
213
350
  const le = (options) => {
214
- const opt = options?.[0] ?? optionsRequired('le');
215
- if (!validateNumberString(opt)) {
216
- optionMustBeNumber('le');
217
- }
351
+ const opt = requiredNumberOption(options, 0, 'le');
218
352
  return (value) => {
219
353
  if (typeof value !== 'number') {
220
354
  valueMustBeNumber('le');
221
355
  }
222
- return value <= Number(opt);
356
+ return value <= opt;
223
357
  };
224
358
  };
225
359
  /**
@@ -229,15 +363,12 @@ const le = (options) => {
229
363
  * @returns Filter function that returns boolean
230
364
  */
231
365
  const gt = (options) => {
232
- const opt = options?.[0] ?? optionsRequired('gt');
233
- if (!validateNumberString(opt)) {
234
- optionMustBeNumber('gt');
235
- }
366
+ const opt = requiredNumberOption(options, 0, 'gt');
236
367
  return (value) => {
237
368
  if (typeof value !== 'number') {
238
369
  valueMustBeNumber('gt');
239
370
  }
240
- return value > Number(opt);
371
+ return value > opt;
241
372
  };
242
373
  };
243
374
  /**
@@ -247,15 +378,12 @@ const gt = (options) => {
247
378
  * @returns Filter function that returns boolean
248
379
  */
249
380
  const ge = (options) => {
250
- const opt = options?.[0] ?? optionsRequired('ge');
251
- if (!validateNumberString(opt)) {
252
- optionMustBeNumber('ge');
253
- }
381
+ const opt = requiredNumberOption(options, 0, 'ge');
254
382
  return (value) => {
255
383
  if (typeof value !== 'number') {
256
384
  valueMustBeNumber('ge');
257
385
  }
258
- return value >= Number(opt);
386
+ return value >= opt;
259
387
  };
260
388
  };
261
389
  /**
@@ -265,15 +393,12 @@ const ge = (options) => {
265
393
  * @returns Filter function that returns incremented number
266
394
  */
267
395
  const inc = (options) => {
268
- const opt = options?.[0] ?? optionsRequired('add');
269
- if (!validateNumberString(opt)) {
270
- optionMustBeNumber('add');
271
- }
396
+ const opt = requiredNumberOption(options, 0, 'add');
272
397
  return (value) => {
273
398
  if (typeof value !== 'number') {
274
399
  valueMustBeNumber('add');
275
400
  }
276
- return value + Number(opt);
401
+ return value + opt;
277
402
  };
278
403
  };
279
404
  /**
@@ -283,15 +408,12 @@ const inc = (options) => {
283
408
  * @returns Filter function that returns decremented number
284
409
  */
285
410
  const dec = (options) => {
286
- const opt = options?.[0] ?? optionsRequired('sub');
287
- if (!validateNumberString(opt)) {
288
- optionMustBeNumber('sub');
289
- }
411
+ const opt = requiredNumberOption(options, 0, 'sub');
290
412
  return (value) => {
291
413
  if (typeof value !== 'number') {
292
414
  valueMustBeNumber('sub');
293
415
  }
294
- return value - Number(opt);
416
+ return value - opt;
295
417
  };
296
418
  };
297
419
  /**
@@ -301,15 +423,12 @@ const dec = (options) => {
301
423
  * @returns Filter function that returns multiplied number
302
424
  */
303
425
  const mul = (options) => {
304
- const opt = options?.[0] ?? optionsRequired('mul');
305
- if (!validateNumberString(opt)) {
306
- optionMustBeNumber('mul');
307
- }
426
+ const opt = requiredNumberOption(options, 0, 'mul');
308
427
  return (value) => {
309
428
  if (typeof value !== 'number') {
310
429
  valueMustBeNumber('mul');
311
430
  }
312
- return value * Number(opt);
431
+ return value * opt;
313
432
  };
314
433
  };
315
434
  /**
@@ -319,15 +438,12 @@ const mul = (options) => {
319
438
  * @returns Filter function that returns divided number
320
439
  */
321
440
  const div = (options) => {
322
- const opt = options?.[0] ?? optionsRequired('div');
323
- if (!validateNumberString(opt)) {
324
- optionMustBeNumber('div');
325
- }
441
+ const opt = requiredNumberOption(options, 0, 'div');
326
442
  return (value) => {
327
443
  if (typeof value !== 'number') {
328
444
  valueMustBeNumber('div');
329
445
  }
330
- return value / Number(opt);
446
+ return value / opt;
331
447
  };
332
448
  };
333
449
  /**
@@ -337,15 +453,12 @@ const div = (options) => {
337
453
  * @returns Filter function that returns remainder
338
454
  */
339
455
  const mod = (options) => {
340
- const opt = options?.[0] ?? optionsRequired('mod');
341
- if (!validateNumberString(opt)) {
342
- optionMustBeNumber('mod');
343
- }
456
+ const opt = requiredNumberOption(options, 0, 'mod');
344
457
  return (value) => {
345
458
  if (typeof value !== 'number') {
346
459
  valueMustBeNumber('mod');
347
460
  }
348
- return value % Number(opt);
461
+ return value % opt;
349
462
  };
350
463
  };
351
464
  /**
@@ -373,16 +486,8 @@ const abs = (_options) => {
373
486
  * @returns Filter function that returns the clamped number
374
487
  */
375
488
  const clamp = (options) => {
376
- const opt1 = options?.[0] ?? optionsRequired('clamp');
377
- if (!validateNumberString(opt1)) {
378
- optionMustBeNumber('clamp');
379
- }
380
- const opt2 = options?.[1] ?? optionsRequired('clamp');
381
- if (!validateNumberString(opt2)) {
382
- optionMustBeNumber('clamp');
383
- }
384
- const min = Number(opt1);
385
- const max = Number(opt2);
489
+ const min = requiredNumberOption(options, 0, 'clamp');
490
+ const max = requiredNumberOption(options, 1, 'clamp');
386
491
  return (value) => {
387
492
  if (typeof value !== 'number') {
388
493
  valueMustBeNumber('clamp');
@@ -397,15 +502,12 @@ const clamp = (options) => {
397
502
  * @returns Filter function that returns formatted string
398
503
  */
399
504
  const fix = (options) => {
400
- const opt = options?.[0] ?? "0";
401
- if (!validateNumberString(opt)) {
402
- optionMustBeNumber('toFixed');
403
- }
505
+ const opt = numberOption(options?.[0] ?? "0", 'toFixed');
404
506
  return (value) => {
405
507
  if (typeof value !== 'number') {
406
508
  valueMustBeNumber('toFixed');
407
509
  }
408
- return value.toFixed(Number(opt));
510
+ return value.toFixed(opt);
409
511
  };
410
512
  };
411
513
  /**
@@ -498,18 +600,10 @@ const trim = (_options) => {
498
600
  * @returns Filter function that returns sliced string
499
601
  */
500
602
  const slice = (options) => {
501
- const numberedOpts = [];
502
- const opt1 = options?.[0] ?? optionsRequired('slice');
503
- if (!validateNumberString(opt1)) {
504
- optionMustBeNumber('slice');
505
- }
506
- numberedOpts.push(Number(opt1));
603
+ const numberedOpts = [requiredNumberOption(options, 0, 'slice')];
507
604
  const opt2 = options?.[1];
508
605
  if (typeof opt2 !== 'undefined') {
509
- if (!validateNumberString(opt2)) {
510
- optionMustBeNumber('slice');
511
- }
512
- numberedOpts.push(Number(opt2));
606
+ numberedOpts.push(numberOption(opt2, 'slice'));
513
607
  }
514
608
  return (value) => {
515
609
  return String(value).slice(...numberedOpts);
@@ -522,16 +616,10 @@ const slice = (options) => {
522
616
  * @returns Filter function that returns substring
523
617
  */
524
618
  const substr = (options) => {
525
- const opt1 = options?.[0] ?? optionsRequired('substr');
526
- if (!validateNumberString(opt1)) {
527
- optionMustBeNumber('substr');
528
- }
529
- const opt2 = options?.[1] ?? optionsRequired('substr');
530
- if (!validateNumberString(opt2)) {
531
- optionMustBeNumber('substr');
532
- }
619
+ const opt1 = requiredNumberOption(options, 0, 'substr');
620
+ const opt2 = requiredNumberOption(options, 1, 'substr');
533
621
  return (value) => {
534
- return String(value).substr(Number(opt1), Number(opt2));
622
+ return String(value).substr(opt1, opt2);
535
623
  };
536
624
  };
537
625
  /**
@@ -541,13 +629,10 @@ const substr = (options) => {
541
629
  * @returns Filter function that returns padded string
542
630
  */
543
631
  const pad = (options) => {
544
- const opt1 = options?.[0] ?? optionsRequired('padStart');
545
- if (!validateNumberString(opt1)) {
546
- optionMustBeNumber('padStart');
547
- }
632
+ const opt1 = requiredNumberOption(options, 0, 'padStart');
548
633
  const opt2 = options?.[1] ?? '0';
549
634
  return (value) => {
550
- return String(value).padStart(Number(opt1), opt2);
635
+ return String(value).padStart(opt1, opt2);
551
636
  };
552
637
  };
553
638
  /**
@@ -557,13 +642,10 @@ const pad = (options) => {
557
642
  * @returns Filter function that returns padded string
558
643
  */
559
644
  const padEnd = (options) => {
560
- const opt1 = options?.[0] ?? optionsRequired('padEnd');
561
- if (!validateNumberString(opt1)) {
562
- optionMustBeNumber('padEnd');
563
- }
645
+ const opt1 = requiredNumberOption(options, 0, 'padEnd');
564
646
  const opt2 = options?.[1] ?? ' ';
565
647
  return (value) => {
566
- return String(value).padEnd(Number(opt1), opt2);
648
+ return String(value).padEnd(opt1, opt2);
567
649
  };
568
650
  };
569
651
  /**
@@ -573,12 +655,9 @@ const padEnd = (options) => {
573
655
  * @returns Filter function that returns repeated string
574
656
  */
575
657
  const rep = (options) => {
576
- const opt = options?.[0] ?? optionsRequired('repeat');
577
- if (!validateNumberString(opt)) {
578
- optionMustBeNumber('repeat');
579
- }
658
+ const opt = requiredNumberOption(options, 0, 'repeat');
580
659
  return (value) => {
581
- return String(value).repeat(Number(opt));
660
+ return String(value).repeat(opt);
582
661
  };
583
662
  };
584
663
  /**
@@ -621,15 +700,11 @@ const float = (_options) => {
621
700
  * @returns Filter function that returns rounded number
622
701
  */
623
702
  const round = (options) => {
624
- const opt = options?.[0] ?? '0';
625
- if (!validateNumberString(opt)) {
626
- optionMustBeNumber('round');
627
- }
703
+ const optValue = Math.pow(10, numberOption(options?.[0] ?? '0', 'round'));
628
704
  return (value) => {
629
705
  if (typeof value !== 'number') {
630
706
  valueMustBeNumber('round');
631
707
  }
632
- const optValue = Math.pow(10, Number(opt));
633
708
  return Math.round(value * optValue) / optValue;
634
709
  };
635
710
  };
@@ -640,15 +715,11 @@ const round = (options) => {
640
715
  * @returns Filter function that returns floored number
641
716
  */
642
717
  const floor = (options) => {
643
- const opt = options?.[0] ?? '0';
644
- if (!validateNumberString(opt)) {
645
- optionMustBeNumber('floor');
646
- }
718
+ const optValue = Math.pow(10, numberOption(options?.[0] ?? '0', 'floor'));
647
719
  return (value) => {
648
720
  if (typeof value !== 'number') {
649
721
  valueMustBeNumber('floor');
650
722
  }
651
- const optValue = Math.pow(10, Number(opt));
652
723
  return Math.floor(value * optValue) / optValue;
653
724
  };
654
725
  };
@@ -659,15 +730,11 @@ const floor = (options) => {
659
730
  * @returns Filter function that returns ceiled number
660
731
  */
661
732
  const ceil = (options) => {
662
- const opt = options?.[0] ?? '0';
663
- if (!validateNumberString(opt)) {
664
- optionMustBeNumber('ceil');
665
- }
733
+ const optValue = Math.pow(10, numberOption(options?.[0] ?? '0', 'ceil'));
666
734
  return (value) => {
667
735
  if (typeof value !== 'number') {
668
736
  valueMustBeNumber('ceil');
669
737
  }
670
- const optValue = Math.pow(10, Number(opt));
671
738
  return Math.ceil(value * optValue) / optValue;
672
739
  };
673
740
  };
@@ -678,15 +745,12 @@ const ceil = (options) => {
678
745
  * @returns Filter function that returns percentage string with '%'
679
746
  */
680
747
  const percent = (options) => {
681
- const opt = options?.[0] ?? '0';
682
- if (!validateNumberString(opt)) {
683
- optionMustBeNumber('percent');
684
- }
748
+ const opt = numberOption(options?.[0] ?? '0', 'percent');
685
749
  return (value) => {
686
750
  if (typeof value !== 'number') {
687
751
  valueMustBeNumber('percent');
688
752
  }
689
- return `${(value * 100).toFixed(Number(opt))}%`;
753
+ return `${(value * 100).toFixed(opt)}%`;
690
754
  };
691
755
  };
692
756
  /**
@@ -701,8 +765,10 @@ const percent = (options) => {
701
765
  * `fix` / `percent`, which already return strings. Rejecting non-numbers here would
702
766
  * break exactly the combination this filter exists for.
703
767
  *
704
- * `null` / `undefined` pass through untouched rather than becoming `"undefinedpx"`,
705
- * so the binding layer's "undefined skips the write, null clears" semantics survive.
768
+ * `null` / `undefined` pass through untouched rather than becoming `"undefinedpx"`, so the
769
+ * binding layer's "undefined skips the write, null clears" semantics survive. That guard is no
770
+ * longer written here: it is `nullishPassthrough`, shared with the rest of the `String(value)`
771
+ * family, which used to paint `"undefined"` into the page for exactly the same reason.
706
772
  *
707
773
  * @param options - Array with the unit/suffix as first element (required)
708
774
  * @returns Filter function that returns the value with the unit appended
@@ -710,9 +776,6 @@ const percent = (options) => {
710
776
  const unit = (options) => {
711
777
  const opt = options?.[0] ?? optionsRequired('unit');
712
778
  return (value) => {
713
- if (value === null || typeof value === 'undefined') {
714
- return value;
715
- }
716
779
  return String(value) + opt;
717
780
  };
718
781
  };
@@ -745,11 +808,7 @@ const join = (options) => {
745
808
  * @returns Filter function that returns the truncated string
746
809
  */
747
810
  const truncate = (options) => {
748
- const opt1 = options?.[0] ?? optionsRequired('truncate');
749
- if (!validateNumberString(opt1)) {
750
- optionMustBeNumber('truncate');
751
- }
752
- const maxLength = Number(opt1);
811
+ const maxLength = requiredNumberOption(options, 0, 'truncate');
753
812
  const suffix = options?.[1] ?? '…';
754
813
  return (value) => {
755
814
  const v = String(value);
@@ -956,17 +1015,18 @@ const builtinFilters = {
956
1015
  "clamp": clamp,
957
1016
  "toFixed": fix,
958
1017
  "locale": locale,
959
- "upper": uc,
960
- "lower": lc,
961
- "capitalize": cap,
962
- "trim": trim,
963
- "slice": slice,
964
- "substr": substr,
965
- "padStart": pad,
966
- "padEnd": padEnd,
967
- "repeat": rep,
968
- "reverse": rev,
969
- "truncate": truncate,
1018
+ // `String(value)` で組み立てる族は、値が無いときに素通しする(要件 B8 — nullishPassthrough を参照)
1019
+ "upper": nullishPassthrough(uc),
1020
+ "lower": nullishPassthrough(lc),
1021
+ "capitalize": nullishPassthrough(cap),
1022
+ "trim": nullishPassthrough(trim),
1023
+ "slice": nullishPassthrough(slice),
1024
+ "substr": nullishPassthrough(substr),
1025
+ "padStart": nullishPassthrough(pad),
1026
+ "padEnd": nullishPassthrough(padEnd),
1027
+ "repeat": nullishPassthrough(rep),
1028
+ "reverse": nullishPassthrough(rev),
1029
+ "truncate": nullishPassthrough(truncate),
970
1030
  "join": join,
971
1031
  "int": int,
972
1032
  "float": float,
@@ -974,7 +1034,7 @@ const builtinFilters = {
974
1034
  "floor": floor,
975
1035
  "ceil": ceil,
976
1036
  "percent": percent,
977
- "unit": unit,
1037
+ "unit": nullishPassthrough(unit),
978
1038
  "date": date,
979
1039
  "time": time,
980
1040
  "datetime": datetime,
@@ -1007,7 +1067,9 @@ const builtinFilterMeta = {
1007
1067
  // 比較・論理
1008
1068
  eq: { description: "等しいか比較", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
1009
1069
  ne: { description: "異なるか比較", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
1010
- not: { description: "ブール値を反転", hasArgs: false, resultType: "boolean", acceptTypes: ["boolean"], minArgs: 0, maxArgs: 0 },
1070
+ // `not` は真偽性(truthy / falsy)の反転。`if:` が `Boolean()` で寄せるのと同じ規則で、
1071
+ // `else:` はこのフィルタを足した束縛として組み立てられる(structural/createNotFilter.ts)
1072
+ not: { description: "真偽性を反転(falsy → true)", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1011
1073
  lt: { description: "より小さいか", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
1012
1074
  le: { description: "以下か", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
1013
1075
  gt: { description: "より大きいか", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
@@ -1029,13 +1091,15 @@ const builtinFilterMeta = {
1029
1091
  capitalize: { description: "先頭文字を大文字に", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1030
1092
  trim: { description: "前後の空白を削除", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1031
1093
  slice: { description: "部分文字列 (start[,end])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
1032
- substr: { description: "部分文字列 (pos,len)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
1033
- padStart: { description: "先頭を埋める (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1034
- padEnd: { description: "末尾を埋める (length[,char])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1094
+ // 長さは省略できない(実装が両方読む)。minArgs: 1 だった頃は補完・lint が `substr(0)` を
1095
+ // 通し、実行時にだけ落ちていた
1096
+ substr: { description: "部分文字列 (pos,len)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 2, maxArgs: 2, argTypes: ["number", "number"] },
1097
+ padStart: { description: "先頭を埋める (length[,char]。char の既定は 0 — JS の既定は空白なので注意)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1098
+ padEnd: { description: "末尾を埋める (length[,char]。char の既定は空白 — JS と同じ)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1035
1099
  repeat: { description: "繰り返し (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
1036
1100
  reverse: { description: "文字順を反転", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
1037
- truncate: { description: "切り詰めて省略記号 (length[,suffix])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1038
- join: { description: "配列を連結 ([separator])", hasArgs: true, resultType: "string", acceptTypes: ["array"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1101
+ truncate: { description: "切り詰めて省略記号 (length[,suffix]。suffix の既定は … — U+2026 の 1 文字)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
1102
+ join: { description: "配列を連結 ([separator]。既定はカンマ + 空白)", hasArgs: true, resultType: "string", acceptTypes: ["array"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1039
1103
  // 数値パース・丸め
1040
1104
  int: { description: "整数にパース", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
1041
1105
  float: { description: "浮動小数点数にパース", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
@@ -1047,11 +1111,13 @@ const builtinFilterMeta = {
1047
1111
  // それらは既に string を返すため(builtinFilters.ts の unit を参照)
1048
1112
  unit: { description: "単位(接尾辞)を付加", hasArgs: true, resultType: "string", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["string"] },
1049
1113
  // 日付・時刻
1050
- date: { description: "ロケール形式の日付", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1051
- time: { description: "ロケール形式の時刻", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1052
- datetime: { description: "ロケール形式の日時", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1053
- ymd: { description: "YYYY-MM-DD 形式", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1054
- hms: { description: "HH:MM:SS 形式", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1114
+ // `locale` と同じくロケールを 1 つ受ける(`date(ja-JP)`)。実装は最初からこれを読んでいたが、
1115
+ // メタデータ側が maxArgs: 0 だったため lint と補完が正しい書き方を誤りとして報告していた
1116
+ date: { description: "ロケール形式の日付 ([locale]。既定は config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1117
+ time: { description: "ロケール形式の時刻 ([locale]。既定は config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1118
+ datetime: { description: "ロケール形式の日時 ([locale]。既定は config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1119
+ ymd: { description: "YYYY-MM-DD 形式 ([separator]。既定は -)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1120
+ hms: { description: "HH:MM:SS 形式 ([separator]。既定は :)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
1055
1121
  // 真偽値・変換
1056
1122
  falsy: { description: "偽値か判定", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
1057
1123
  truthy: { description: "真値か判定", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
@@ -1070,82 +1136,6 @@ const STRUCTURAL_BINDING_TYPE_SET = new Set([
1070
1136
  "for",
1071
1137
  ]);
1072
1138
 
1073
- const DELIMITER = '.';
1074
- const WILDCARD = '*';
1075
- const MAX_WILDCARD_DEPTH = 128;
1076
- // data-wcs バインディング構文 `[prop][#mod]: [path][|filter...]` の区切り文字(単一正本・`@state` は v2 で撤去)。
1077
- // これらは「死守の壁(構文契約)」であり値は不変。manifest.syntax.delimiters で公開される。
1078
- const BINDING_SEPARATOR = ';'; // 複数バインディングの区切り
1079
- const PROP_VALUE_SEPARATOR = ':'; // 左辺(prop)と右辺(path)の区切り
1080
- const MODIFIER_SEPARATOR = '#'; // prop と修飾子の区切り
1081
- const FILTER_SEPARATOR = '|'; // フィルタパイプの区切り
1082
- // 修飾子(`#` 後)の語彙(単一正本)。manifest.syntax.modifiers で公開される。
1083
- // フラグ形(`#prevent` — 値を取らない)とキー値形(`#init=element` — `=` で値を取る)。
1084
- // 消費箇所(event/handler・BindingSession・twowayHandler・bindings/initialSync)は
1085
- // この定数を参照する — 文字列リテラルの散在は tooling への収載漏れの温床だった
1086
- // (docs/static-wiring-dx-design.md §2-2)。
1087
- const MODIFIER_PREVENT = 'prevent';
1088
- const MODIFIER_STOP = 'stop';
1089
- const MODIFIER_READONLY = 'ro';
1090
- const MODIFIER_FLAGS = Object.freeze([
1091
- MODIFIER_PREVENT, MODIFIER_STOP, MODIFIER_READONLY,
1092
- ]);
1093
- const MODIFIER_KEY_INIT = 'init';
1094
- const MODIFIER_KEY_SYNC = 'sync';
1095
- const MODIFIER_KEYS = Object.freeze([
1096
- MODIFIER_KEY_INIT, MODIFIER_KEY_SYNC,
1097
- ]);
1098
- // bindingType 判別と左辺 namespace の語彙(単一正本)。manifest.syntax.bindingTypes で
1099
- // 公開される。パーサ(parseBindTextsForElement)とイベント層はこの定数に分岐する。
1100
- // apply 層のディスパッチマップ(apply/applyChange.ts の applyChangeByFirstSegment)の
1101
- // キー集合との一致は __tests__/manifest.test.ts の drift テストが強制する —
1102
- // manifest エントリ(DOM 非依存)から apply 層を import しないための分離。
1103
- const ELSE_KEYWORD = 'else';
1104
- const SPREAD_PROP = '...';
1105
- const EVENT_PROP_PREFIX = 'on';
1106
- const EVENT_TOKEN_NAMESPACE = 'eventToken';
1107
- const COMMAND_NAMESPACE = 'command';
1108
- const CLASS_NAMESPACE = 'class';
1109
- const ATTR_NAMESPACE = 'attr';
1110
- const STYLE_NAMESPACE = 'style';
1111
- // リストインデックス参照名(`$1`..`$N`)の接頭辞(単一正本)。
1112
- // manifest.syntax.indexParam で公開される。
1113
- const INDEX_PARAM_PREFIX = '$';
1114
- /**
1115
- * stackIndexByIndexName
1116
- * インデックス名からスタックインデックスへのマッピング
1117
- * $1 => 0
1118
- * $2 => 1
1119
- * :
1120
- * ${i + 1} => i
1121
- * i < MAX_WILDCARD_DEPTH
1122
- */
1123
- const tmpIndexByIndexName = {};
1124
- for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
1125
- tmpIndexByIndexName[`${INDEX_PARAM_PREFIX}${i + 1}`] = i;
1126
- }
1127
- Object.freeze(tmpIndexByIndexName);
1128
- const STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
1129
- const STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
1130
- /** 旧名 `$updatedCallback` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
1131
- const STATE_UPDATED_CALLBACK_NAME = "$renderedCallback";
1132
- const STATE_ERROR_CALLBACK_NAME = "$errorCallback";
1133
- const WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
1134
- const STATE_BINDABLES_NAME = "$bindables";
1135
- const STATE_COMMANDS_NAME = "$commands";
1136
- const STATE_COMMAND_TOKENS_NAME = "$commandTokens";
1137
- const STATE_COMMAND_NAMESPACE_NAME = "$command";
1138
- const STATE_EVENT_TOKENS_NAME = "$eventTokens";
1139
- const STATE_ON_NAME = "$on";
1140
- /** 旧名 `$streams` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
1141
- const STATE_STREAMS_NAME = "$stream";
1142
- const STATE_WATCH_NAME = "$watch";
1143
- const STATE_SCAN_NAME = "$scan";
1144
- const STATE_RECURSION_NAME = "$recursion";
1145
- const STATE_LIST_KEYS_NAME = "$listKeys";
1146
- const STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
1147
- const STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
1148
-
1149
1139
  /**
1150
1140
  * manifest.ts — `<wcs-state>` の構文・フィルタ・予約名を機械可読な単一正本として公開する。
1151
1141
  *
@@ -1159,8 +1149,23 @@ const STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
1159
1149
  * - 将来 `dist/wcs-manifest.json` としてビルド時に書き出し、vscode-wcs がそれを読む形に発展させる。
1160
1150
  * - ドリフト検出テスト(__tests__/manifest.test.ts)が、フィルタ集合の golden と実装の一致を CI で保証する。
1161
1151
  */
1162
- /** マニフェストのバージョン(構造を変えたら上げる)。 */
1163
- const WCS_MANIFEST_VERSION = 1;
1152
+ /**
1153
+ * マニフェストのバージョン(構造を変えたら上げる)。
1154
+ *
1155
+ * 3.1 で `syntax.bindingTypes.explicitPropertyPrefix`、3.2 で `filterAliases`、
1156
+ * 3.x の次で `declarationAliases` / `apiAliases` を足したので 2。
1157
+ * 消費側(vscode-wcs)はまだこの定数を参照していないが、公開している以上ドリフトさせない。
1158
+ */
1159
+ const WCS_MANIFEST_VERSION = 2;
1160
+ /**
1161
+ * state API の旧名 → 正式名(要件 B12・docs/state-3x-naming.ja.md)。正本は
1162
+ * `proxy/traps/get.ts` の case ラベルで、ここはそれを機械可読にした写し
1163
+ * (一致は `__tests__/manifest.test.ts` が固定する)。
1164
+ */
1165
+ const STATE_API_ALIASES = Object.freeze({
1166
+ $trackDependency: "$dependOn",
1167
+ $untrackDependency: "$untracked",
1168
+ });
1164
1169
  /** 機械可読な単一正本を返す。vscode-wcs はこれを消費する想定。 */
1165
1170
  function getWcsManifest() {
1166
1171
  return {
@@ -1205,10 +1210,12 @@ function getWcsManifest() {
1205
1210
  filters: Object.keys(outputBuiltinFilters),
1206
1211
  filterMeta: builtinFilterMeta,
1207
1212
  filterAliases: builtinFilterAliases,
1213
+ declarationAliases: DECLARATION_ALIASES,
1214
+ apiAliases: STATE_API_ALIASES,
1208
1215
  reservedLifecycle: [
1209
1216
  STATE_CONNECTED_CALLBACK_NAME,
1210
1217
  STATE_DISCONNECTED_CALLBACK_NAME,
1211
- STATE_UPDATED_CALLBACK_NAME,
1218
+ STATE_RENDERED_CALLBACK_NAME,
1212
1219
  STATE_ERROR_CALLBACK_NAME,
1213
1220
  WEBCOMPONENT_STATE_READY_CALLBACK_NAME,
1214
1221
  ],
@@ -1219,7 +1226,7 @@ function getWcsManifest() {
1219
1226
  STATE_COMMAND_NAMESPACE_NAME,
1220
1227
  STATE_EVENT_TOKENS_NAME,
1221
1228
  STATE_ON_NAME,
1222
- STATE_STREAMS_NAME,
1229
+ STATE_STREAM_NAME,
1223
1230
  STATE_WATCH_NAME,
1224
1231
  STATE_SCAN_NAME,
1225
1232
  STATE_LIST_KEYS_NAME,
@@ -1230,4 +1237,4 @@ function getWcsManifest() {
1230
1237
  };
1231
1238
  }
1232
1239
 
1233
- export { STRUCTURAL_BINDING_TYPE_SET, WCS_MANIFEST_VERSION, builtinFilterAliases, builtinFilterMeta, getWcsManifest };
1240
+ export { DECLARATION_ALIASES, STATE_API_ALIASES, STRUCTURAL_BINDING_TYPE_SET, WCS_MANIFEST_VERSION, builtinFilterAliases, builtinFilterMeta, getWcsManifest };