base-paint 0.1.0 → 0.2.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 (60) hide show
  1. package/dist/charts.d.ts +62 -0
  2. package/dist/charts.d.ts.map +1 -0
  3. package/dist/charts.js +1677 -0
  4. package/dist/charts.js.map +1 -0
  5. package/dist/contract.d.ts.map +1 -1
  6. package/dist/contract.js +7 -1
  7. package/dist/contract.js.map +1 -1
  8. package/dist/controls.d.ts +114 -0
  9. package/dist/controls.d.ts.map +1 -0
  10. package/dist/controls.js +724 -0
  11. package/dist/controls.js.map +1 -0
  12. package/dist/help.d.ts +24 -0
  13. package/dist/help.d.ts.map +1 -0
  14. package/dist/help.js +604 -0
  15. package/dist/help.js.map +1 -0
  16. package/dist/index.d.ts +21 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +21 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/spec/charts.d.ts +222 -0
  21. package/dist/spec/charts.d.ts.map +1 -0
  22. package/dist/spec/charts.js +35 -0
  23. package/dist/spec/charts.js.map +1 -0
  24. package/dist/spec/controls.d.ts +329 -0
  25. package/dist/spec/controls.d.ts.map +1 -0
  26. package/dist/spec/controls.js +119 -0
  27. package/dist/spec/controls.js.map +1 -0
  28. package/dist/spec/help.d.ts +353 -0
  29. package/dist/spec/help.d.ts.map +1 -0
  30. package/dist/spec/help.js +167 -0
  31. package/dist/spec/help.js.map +1 -0
  32. package/dist/spec/index.d.ts +40 -0
  33. package/dist/spec/index.d.ts.map +1 -0
  34. package/dist/spec/index.js +164 -0
  35. package/dist/spec/index.js.map +1 -0
  36. package/dist/spec/style.d.ts +46 -0
  37. package/dist/spec/style.d.ts.map +1 -0
  38. package/dist/spec/style.js +39 -0
  39. package/dist/spec/style.js.map +1 -0
  40. package/dist/spec/template.d.ts +363 -0
  41. package/dist/spec/template.d.ts.map +1 -0
  42. package/dist/spec/template.js +252 -0
  43. package/dist/spec/template.js.map +1 -0
  44. package/dist/spec/text.d.ts +157 -0
  45. package/dist/spec/text.d.ts.map +1 -0
  46. package/dist/spec/text.js +83 -0
  47. package/dist/spec/text.js.map +1 -0
  48. package/dist/style.d.ts +19 -0
  49. package/dist/style.d.ts.map +1 -1
  50. package/dist/style.js +923 -0
  51. package/dist/style.js.map +1 -1
  52. package/dist/template.d.ts +27 -0
  53. package/dist/template.d.ts.map +1 -0
  54. package/dist/template.js +362 -0
  55. package/dist/template.js.map +1 -0
  56. package/dist/text.d.ts +19 -0
  57. package/dist/text.d.ts.map +1 -0
  58. package/dist/text.js +448 -0
  59. package/dist/text.js.map +1 -0
  60. package/package.json +2 -2
@@ -0,0 +1,363 @@
1
+ /** base-paint/spec/template:占位符契约与统一填充器(#74 冻结面)。
2
+ *
3
+ * 本文件是**冻结的共享层契约类型**(type-only + 纯数据常量),与
4
+ * `src/contract.ts`(既有渲染契约 envelope→HTML)**不同物**。
5
+ *
6
+ * 命名红线(AC-1):`injector` 一词专指 sidebar 槽位装配
7
+ *(`src/injector.ts`:mountInjector/SlotsPort/openPage)。HTML 占位符
8
+ * 填充器对外名 **fillTemplate**,落点 `src/template.ts`。
9
+ * 禁止用法:htmlInjector/injectHtml/injectPlaceholders/htmlTemplateInjector。
10
+ *
11
+ * 依赖红线(AC-13):本文件只许 `import type`;base-paint 不得运行时依赖
12
+ * base-link-core(tooling/check-boundaries.mjs L15)。
13
+ */
14
+ /** 六个占位符的逐字写法(唯一真相源;文档 §3.1 是它的投影)。
15
+ *
16
+ * `content` 是 #118 收归契约的**正文槽位**(`<!--CONTENT-->`)。溯源:旧基线
17
+ * `D:/2Study/StudyNotes/SKILLS/卡路里` 73 个模板**零命中**——它不是旧 v1.30 能力,
18
+ * 而是**新架构发明的槽位**,由本仓 4 个技能(bill/chef/home/schedule)各自私有
19
+ * `CONTENT_MARKER` 事实使用(53 个内容页),#118 把它收归共享层唯一真相源。
20
+ */
21
+ export declare const TEMPLATE_MARKERS: Readonly<{
22
+ readonly injectData: "<!--INJECT-DATA-->";
23
+ readonly content: "<!--CONTENT-->";
24
+ readonly sharedHelpers: "<!--SHARED-HELPERS-->";
25
+ readonly sharedCss: "<!--SHARED-CSS-->";
26
+ readonly chartsHelpers: "<!--CHARTS-HELPERS-->";
27
+ readonly noShared: "<!--NO-SHARED-->";
28
+ }>;
29
+ export type TemplateMarkerKey = keyof typeof TEMPLATE_MARKERS;
30
+ export type TemplateMarkerLiteral = (typeof TEMPLATE_MARKERS)[TemplateMarkerKey];
31
+ /** 数量规则:exactly-one 硬拦截;zero-or-one 可选;zero-or-one-exempt 豁免通道。 */
32
+ export type MarkerRule = 'exactly-one' | 'zero-or-one' | 'zero-or-one-exempt';
33
+ export interface MarkerRuleSpec {
34
+ readonly literal: string;
35
+ readonly rule: MarkerRule;
36
+ /** 未声明 NO-SHARED 时是否必须出现。 */
37
+ readonly required: boolean;
38
+ /** 声明 NO-SHARED 后是否允许缺席。 */
39
+ readonly exemptable: boolean;
40
+ }
41
+ /** 每个标记的逐字写法与数量约束(AC-6/AC-12/#118 D-1…D-2)。
42
+ *
43
+ * `injectData` 由 `exactly-one`/`required: true` **放宽**为 `zero-or-one`/`required: false`
44
+ * ——这是 #118 授权放宽的既有签名之一(第 2 处为 `FillTemplateInput.data?`,D-8 裁定 2),理由:数据页与内容页是**互斥的两类模板**,
45
+ * 数量约束改由载荷槽规则 `PAYLOAD_SLOT_RULE` 承担(恰有其一)。
46
+ */
47
+ export declare const MARKER_RULES: Readonly<{
48
+ readonly injectData: {
49
+ readonly literal: "<!--INJECT-DATA-->";
50
+ readonly rule: "zero-or-one";
51
+ readonly required: false;
52
+ readonly exemptable: false;
53
+ };
54
+ readonly content: {
55
+ readonly literal: "<!--CONTENT-->";
56
+ readonly rule: "zero-or-one";
57
+ readonly required: false;
58
+ readonly exemptable: false;
59
+ };
60
+ readonly sharedHelpers: {
61
+ readonly literal: "<!--SHARED-HELPERS-->";
62
+ readonly rule: "exactly-one";
63
+ readonly required: true;
64
+ readonly exemptable: true;
65
+ };
66
+ readonly sharedCss: {
67
+ readonly literal: "<!--SHARED-CSS-->";
68
+ readonly rule: "exactly-one";
69
+ readonly required: true;
70
+ readonly exemptable: true;
71
+ };
72
+ readonly chartsHelpers: {
73
+ readonly literal: "<!--CHARTS-HELPERS-->";
74
+ readonly rule: "zero-or-one";
75
+ readonly required: false;
76
+ readonly exemptable: false;
77
+ };
78
+ readonly noShared: {
79
+ readonly literal: "<!--NO-SHARED-->";
80
+ readonly rule: "zero-or-one-exempt";
81
+ readonly required: false;
82
+ readonly exemptable: false;
83
+ };
84
+ }>;
85
+ /** 载荷槽规则(#118 D-2/A2,**机读**):`injectData`(数据页)与 `content`(内容页)**恰有其一**。
86
+ *
87
+ * - 两者都有 → `conflictCode`(`marker-conflict`);
88
+ * - 两者都无 → `missingCode`(`marker-missing`);
89
+ * - 恰有其一 → 合法(再按 `TEMPLATE_KIND_RULE` 定分型)。
90
+ *
91
+ * 事实依据:全仓 65 个模板**零冲突**(无任何一个同时含两标记,见
92
+ * `docs/research/t118-template-inventory.md` §2,**已入仓**;原 `.scratch/t118/template-inventory.md`
93
+ * 是归档前旧位置、勿据此取件);6 个数据页的 `INJECT-DATA` 全部落在
94
+ * 自带容器内,53 个内容页的 `CONTENT` 全部裸在 `:12`。
95
+ *
96
+ * 与 `TEMPLATE_CHECK_ORDER` 的关系:两槽**皆有**时由本规则报 `conflictCode`;两槽**皆无**时
97
+ * 由本规则报 `missingCode`。若同一模板还命中别的条件,取 `TEMPLATE_CHECK_ORDER` 里**更靠前**的码
98
+ * (首个命中即抛、不聚合)。
99
+ */
100
+ export declare const PAYLOAD_SLOT_RULE: Readonly<{
101
+ readonly members: readonly ["injectData", "content"];
102
+ readonly rule: "exactly-one";
103
+ readonly conflictCode: "marker-conflict";
104
+ readonly missingCode: "marker-missing";
105
+ }>;
106
+ /** 模板三分型(#118 D-6/A5):数据页/内容页/遗留(契约外资产)。 */
107
+ export declare const TEMPLATE_KINDS: readonly ["data-page", "content-page", "legacy"];
108
+ export type TemplateKind = (typeof TEMPLATE_KINDS)[number];
109
+ /** 分型判定规则(**机读**):`required` 各恰 1 次且 `forbidden` 各 0 次即命中该型。
110
+ *
111
+ * 三型**穷尽且互斥**——但**仅对合法模板(计数 ∈ {0,1})成立**:恰有其一载荷槽 →
112
+ * `data-page`/`content-page`;两者皆无 → `legacy`;两者皆有 → 三型皆不命中,报
113
+ * `noKindCode`(三型同值,恒等于 `PAYLOAD_SLOT_RULE.conflictCode` = `marker-conflict`)。
114
+ * 计数 > 1 的模板**不属分型判定面**(本规则只对 0/1 次计数试配),由 `TEMPLATE_CHECK_ORDER`
115
+ * 的**首个命中码**报错(数量 > 1 → `marker-duplicate`,见契约 §3.1.2⑤)。
116
+ *
117
+ * `legacy` 的口径:**契约外的遗留资产**——按本规则判定的「两载荷槽皆无」,调用 `fillTemplate`
118
+ * 抛 `marker-missing` 是**正确行为**(不该能填);**payload 容器不是分型判据**(容器只由
119
+ * `CONTAINER_CHECK_RULE` 在含 `injectData` 时校验)。本仓 6 个 calorie 模板属此型,
120
+ * 处置归 #107(DESIGN D-3)。
121
+ */
122
+ export declare const TEMPLATE_KIND_RULE: Readonly<{
123
+ readonly 'data-page': {
124
+ readonly required: readonly ["injectData"];
125
+ readonly forbidden: readonly ["content"];
126
+ readonly noKindCode: "marker-conflict";
127
+ };
128
+ readonly 'content-page': {
129
+ readonly required: readonly ["content"];
130
+ readonly forbidden: readonly ["injectData"];
131
+ readonly noKindCode: "marker-conflict";
132
+ };
133
+ readonly legacy: {
134
+ readonly required: readonly [];
135
+ readonly forbidden: readonly ["injectData", "content"];
136
+ readonly noKindCode: "marker-conflict";
137
+ };
138
+ }>;
139
+ /** 注入顺序(逐字对齐旧 §7「SHARED(JS) → SHARED-CSS → CHARTS → DATA」)。
140
+ *
141
+ * `content` **不在**本数组内(#118 显式记账):`INJECTION_ORDER` 是**既有冻结签名**、
142
+ * 只许追加不允许改。**正文替换与四个资产/数据步之间无先后语义**(#118 FX-6 统一口径):
143
+ * 载荷槽两成员**互斥**(`PAYLOAD_SLOT_RULE`),任一模板至多出现 `content` 与 `injectData` 之一,
144
+ * 故两者不会对同一文本竞争同一位置;`content` 既不占本数组的位置,**也不是**「恒为最后一步」。
145
+ */
146
+ export declare const INJECTION_ORDER: readonly ["sharedHelpers", "sharedCss", "chartsHelpers", "injectData"];
147
+ export type InjectionStep = (typeof INJECTION_ORDER)[number];
148
+ /** 共享资产的包裹约定(#118 D-5/A4,**方案 i**:资产裸文本 + 填充器负责包裹)。
149
+ *
150
+ * 两个**不变量**(违反即抛 `TemplateError`,判定谓词见 `WRAP_PREDICATES`):
151
+ * ① **资产不得自带包裹标签**——`TemplateAssets` 三个值恒为裸文本,违者报 `assetWrappedCode`;
152
+ * ② **三个共享资产标记不得被预包裹**——作用域**限定为 `ASSET_MARKER_KEYS` 的值集**
153
+ * (`sharedCss`/`sharedHelpers`/`chartsHelpers`),**排除 `injectData`**:payload 容器是
154
+ * **必需项**(`CONTAINER_CHECK_RULE`),不是「预包裹」;`content`/`noShared` 不在作用域内。
155
+ * 违者报 `markerPreWrappedCode`。
156
+ *
157
+ * 与旧基线的**偏离(显式记账)**:旧 `公共组件/README.md:63` 规定「占位符必须放在独立
158
+ * `<script>`/`<style>` 块内」(模板自带包裹),本契约**有意偏离**为填充器包裹;理由与
159
+ * 迁移代价见契约 §4.5(迁移面 12 个模板 vs 53~59 个)。parity 不受影响:两种约定产出的
160
+ * HTML 可完全一致。
161
+ */
162
+ export declare const ASSET_WRAP_RULE: Readonly<{
163
+ /** 不变量①:三个资产文本不得自带包裹标签。 */
164
+ readonly assetsBare: true;
165
+ /** 包裹标签由填充器统一写;模板与技能侧不得预包。 */
166
+ readonly fillerWraps: true;
167
+ /** 不变量②:三个共享资产标记不得被预包裹(作用域见 `WRAP_PREDICATES`)。 */
168
+ readonly forbidPreWrappedMarker: true;
169
+ /** 违反不变量①的错误码(资产不可用)。 */
170
+ readonly assetWrappedCode: "asset-missing";
171
+ /** 违反不变量②的错误码(标记与包裹约定冲突)。 */
172
+ readonly markerPreWrappedCode: "marker-conflict";
173
+ }>;
174
+ /** 三个资产的**逐字**包裹标签(唯一真相源;键与 `TemplateAssets` 对齐)。
175
+ *
176
+ * 填充器据此把裸资产包成:`sharedCssText` → `<style>` + 文本 + `</style>`;
177
+ * `sharedHelpersJs`/`chartsHelpersJs` → `<script>` + 文本 + `</script>`。
178
+ * 只许引本常量,不得在实现里另写字面量(第二真相)。
179
+ */
180
+ export declare const ASSET_WRAPPERS: Readonly<{
181
+ readonly sharedCssText: {
182
+ readonly openTag: "<style>";
183
+ readonly closeTag: "</style>";
184
+ };
185
+ readonly sharedHelpersJs: {
186
+ readonly openTag: "<script>";
187
+ readonly closeTag: "</script>";
188
+ };
189
+ readonly chartsHelpersJs: {
190
+ readonly openTag: "<script>";
191
+ readonly closeTag: "</script>";
192
+ };
193
+ }>;
194
+ /** 资产键 → 标记键(**唯一映射**,避免「资产键」与「标记键」各写一份、漂移无从发现)。
195
+ *
196
+ * 不变量②的作用域按**标记键**表述(`WRAP_PREDICATES.forbidPreWrappedMarker.scope`),
197
+ * 而包裹标签按**资产键**表述(`ASSET_WRAPPERS`)——本常量是两者之间唯一的桥。
198
+ */
199
+ export declare const ASSET_MARKER_KEYS: Readonly<{
200
+ readonly sharedCssText: "sharedCss";
201
+ readonly sharedHelpersJs: "sharedHelpers";
202
+ readonly chartsHelpersJs: "chartsHelpers";
203
+ }>;
204
+ /** 两条包裹不变量的**判定谓词**(#118 FX-3,机读;**唯一一份实现**)。
205
+ *
206
+ * 作用域(`scope`)+ 判定方式(`method`)+ 错误码(`code`)三者由本常量冻结:
207
+ *
208
+ * ① `assetsBare`(不变量①,`ASSET_WRAP_RULE.assetsBare`):`scope` = `ASSET_WRAPPERS` 的键集
209
+ * (三个资产文本)。判定方式 `trim-prefix-or-suffix` = **资产文本 `trim()` 后,以任一
210
+ * `ASSET_WRAPPERS[*].openTag` 起 或 以任一 `closeTag` 止** → 违反。
211
+ * **不得**用子串命中(`includes`):helpers JS 合法含 `<script>` 字面量(拼接 HTML),
212
+ * 子串判定会误报。仅在该资产**被消费的标记存在**时校验(豁免缺席不校验)。
213
+ * ② `forbidPreWrappedMarker`(不变量②,`ASSET_WRAP_RULE.forbidPreWrappedMarker`):
214
+ * `scope` = `ASSET_MARKER_KEYS` 的值集(三个共享资产标记),`excludes` = `['injectData']`。
215
+ * 判定方式 `enclosing-open-tag` = 对**每一个**标记出现处,扫描其**左侧全部前缀文本**
216
+ * (不限同行,故同行与跨行两种形态都命中):若最近的未闭合 `<style`/`<script` 开标签
217
+ * 存在(即 `lastIndexOf(openTag 的标签名形式) > lastIndexOf(对应 closeTag)`)→ 违反。
218
+ *
219
+ * #74 与 `tooling/classify-templates.mjs` **共用**本常量;**禁止第二份实现**(契约 §3.1.2④)。
220
+ */
221
+ export declare const WRAP_PREDICATES: Readonly<{
222
+ readonly assetsBare: {
223
+ readonly scope: readonly ["sharedCssText", "sharedHelpersJs", "chartsHelpersJs"];
224
+ readonly method: "trim-prefix-or-suffix";
225
+ readonly code: "asset-missing";
226
+ };
227
+ readonly forbidPreWrappedMarker: {
228
+ readonly scope: readonly ["sharedCss", "sharedHelpers", "chartsHelpers"];
229
+ readonly excludes: readonly ["injectData"];
230
+ readonly method: "enclosing-open-tag";
231
+ readonly code: "marker-conflict";
232
+ };
233
+ }>;
234
+ /** payload 容器**由模板自带**,逐字形态(旧 `injector.py:119-120` 同形态):
235
+ * `<script id="payload" type="application/json"><!--INJECT-DATA--></script>`。
236
+ *
237
+ * 填充器**只把 `<!--INJECT-DATA-->` 标记文本替换为 JSON 字符串**,**不生成/不补写容器标签**
238
+ * (生成包裹会与自带容器双包)。本两个常量的用途是**校验模板自带容器**:容器存在则断言
239
+ * id/type 与 `DEFAULT_DATA_SCRIPT_ID`/`DATA_SCRIPT_TYPE`(或 `FillTemplateInput.dataScriptId`)匹配;
240
+ * 容器缺失或 id/type 不符 → 抛 `TemplateError` code `container-missing`(message 区分两因)。
241
+ */
242
+ export declare const DEFAULT_DATA_SCRIPT_ID = "payload";
243
+ export declare const DATA_SCRIPT_TYPE = "application/json";
244
+ /** 容器校验的作用域(#118 FX-1,机读):**仅当模板含 `appliesWhenMarker`(`injectData`)时执行**。
245
+ *
246
+ * 依据:53 个内容页**没有** payload 容器且要求模板零改动(§3.1.2④①),按无条件容器校验会
247
+ * 使 53/65 模板全抛 `container-missing`。因此条文与 `data`/`strict` 的收窄写法对齐:
248
+ * **无 `<!--INJECT-DATA-->` → 不校验容器 → 不可能抛 `container-missing`**。
249
+ * `openTag`/`closeTag` 复用 `ASSET_WRAPPERS`(容器与 JS 包裹同为一对 `<script>`/`</script>`,
250
+ * 不另写字面量);`id`/`type` 复用 `DEFAULT_DATA_SCRIPT_ID`/`DATA_SCRIPT_TYPE`。
251
+ */
252
+ export declare const CONTAINER_CHECK_RULE: Readonly<{
253
+ readonly appliesWhenMarker: "injectData";
254
+ readonly openTag: "<script>";
255
+ readonly closeTag: "</script>";
256
+ readonly id: "payload";
257
+ readonly type: "application/json";
258
+ readonly code: "container-missing";
259
+ }>;
260
+ /** `--strict` 时对 data 做零依赖信封校验的字段表(AC-13:base-paint 自持)。 */
261
+ export declare const STRICT_ENVELOPE_FIELDS: readonly ["version", "skill", "shape", "key", "data"];
262
+ /** 与 base-link-core `ENVELOPE_SHAPES` 逐字一致,漂移由签名测试钉死。 */
263
+ export declare const STRICT_ENVELOPE_SHAPES: readonly ["list", "detail", "stat", "receipt", "analysis", "fallback"];
264
+ /** 填充器错误码(失败一律抛错,不返空页)。
265
+ *
266
+ * `data-missing` = `data === undefined` 或不可 JSON 序列化(洞 12 口径);
267
+ * `content-missing` = 模板含 `<!--CONTENT-->` 但调用方未提供 `content`(#118 D-5,与 `data-missing` 对称);
268
+ * `container-missing` = 模板含 `<!--INJECT-DATA-->` 但缺自带容器,或容器 id/type 与声明不符
269
+ * (`CONTAINER_CHECK_RULE`;**无 `INJECT-DATA` 的模板不校验容器**,FX-1)。
270
+ *
271
+ * **一码多义必须由 `message` 辨因**(#118 FX-10①,不改码):
272
+ * - `asset-missing`:① 资产为空串 ② 资产**自带包裹标签**(不变量①,`WRAP_PREDICATES.assetsBare`);
273
+ * - `marker-conflict`:① 两载荷槽皆有 ② `NO-SHARED` 与 SHARED 并存 ③ 标记被**预包裹**
274
+ * (不变量②,`WRAP_PREDICATES.forbidPreWrappedMarker`)。
275
+ *
276
+ * 只**追加**(#118 A3):既有 7 个逐字、逐序不变,`content-missing` 追加在**末尾**。
277
+ */
278
+ export declare const TEMPLATE_ERROR_CODES: readonly ["marker-missing", "marker-duplicate", "marker-conflict", "data-missing", "container-missing", "asset-missing", "strict-invalid", "content-missing"];
279
+ export type TemplateErrorCode = (typeof TEMPLATE_ERROR_CODES)[number];
280
+ /** **唯一判定次序**(#118 FX-2,机读):8 个错误码的先后,**首个命中即抛、不聚合**。
281
+ *
282
+ * 填充器按本数组顺序逐阶段判定,命中即抛对应 `code`;同一输入同时满足多个条件时,
283
+ * **只有本数组里最靠前的那个码**被抛出(不聚合、不并发报告)。
284
+ *
285
+ * 逐阶段触发条件(契约 §3.1.2⑤ 逐条对齐):
286
+ * ① `marker-duplicate`:任一标记出现 > 1 次(`MARKER_RULES` 数量规则);
287
+ * ② `marker-missing`:必需标记缺席且未豁免,或载荷槽两成员皆无(`PAYLOAD_SLOT_RULE.missingCode`);
288
+ * ③ `marker-conflict`:两载荷槽皆有(`PAYLOAD_SLOT_RULE.conflictCode`)/`NO-SHARED` 与 SHARED 并存/
289
+ * 共享资产标记被预包裹(`WRAP_PREDICATES.forbidPreWrappedMarker`);
290
+ * ④ `container-missing`:**含 `injectData` 时**缺自带容器或 id/type 不符(`CONTAINER_CHECK_RULE`);
291
+ * ⑤ `asset-missing`:被消费的资产为空串或自带包裹标签(`WRAP_PREDICATES.assetsBare`);
292
+ * ⑥ `data-missing`:数据页 `data === undefined` 或不可 JSON 序列化;
293
+ * ⑦ `content-missing`:内容页 `content === undefined`;
294
+ * ⑧ `strict-invalid`:`strict: true` 且信封校验失败。
295
+ *
296
+ * 与既有措辞的一致性:本次序使真实 calorie 模板(两载荷槽皆无 + SHARED-CSS 预包裹)
297
+ * 抛 **`marker-missing`**(② 早于 ③),与 §6.1/§3.1.2③ 的点名一致。
298
+ */
299
+ export declare const TEMPLATE_CHECK_ORDER: readonly ["marker-duplicate", "marker-missing", "marker-conflict", "container-missing", "asset-missing", "data-missing", "content-missing", "strict-invalid"];
300
+ export interface TemplateErrorShape {
301
+ readonly name: 'TemplateError';
302
+ readonly code: TemplateErrorCode;
303
+ readonly marker?: TemplateMarkerKey;
304
+ readonly message: string;
305
+ }
306
+ /** 注入物:base-paint 产出的共享 JS/CSS 文本(技能不得自填,B3)。
307
+ *
308
+ * 三个资产的**唯一产出签名**(各只有一个产出者,#74 只消费):
309
+ * - `sharedHelpersJs` ← `buildSharedHelpersJs(input?)`(§3.3,归 #76);
310
+ * - `sharedCssText` ← `buildStyleSheet().css`(§3.2,归 #75);
311
+ * - `chartsHelpersJs` ← `buildChartsHelpersJs(input?)`(§3.5,归 #78,FX-22)。
312
+ * 三者为空串 → `asset-missing`(产出者为空串视为实现缺陷);产出 JS 文本的内容契约见
313
+ * `SHARED_HELPERS_JS_RULE`(§3.3,FX-18)。
314
+ */
315
+ export interface TemplateAssets {
316
+ readonly sharedHelpersJs: string;
317
+ readonly sharedCssText: string;
318
+ readonly chartsHelpersJs?: string;
319
+ }
320
+ export interface FillTemplateInput {
321
+ readonly template: string;
322
+ readonly assets: TemplateAssets;
323
+ /** 场景数据:由技能包提供,填充器只注入与校验,不生产数据(AC-12)。
324
+ *
325
+ * **数据页提供;内容页可省略**(#118 D-8 裁定 2:载荷槽模型使 `data` 与 `content`
326
+ * **同为条件字段**,故由必填放宽为 `data?: unknown`,与 `content?` 对称;属**追加式放宽**、
327
+ * 向后兼容)。
328
+ * #118 口径:**只在模板含 `<!--INJECT-DATA-->` 时**校验(缺失/不可序列化 → `data-missing`);
329
+ * 内容页(含 `<!--CONTENT-->`)忽略本字段、也不读它。
330
+ */
331
+ readonly data?: unknown;
332
+ /** true = 除数量规则外,追加信封结构校验(旧 `--strict-payload` 语义)。 */
333
+ readonly strict?: boolean;
334
+ /** 期望的容器 id;缺省 `DEFAULT_DATA_SCRIPT_ID`(`'payload'`)。**只用于校验自带容器**
335
+ * (`CONTAINER_CHECK_RULE.id`)——`FillTemplateReport` 无该字段,**不**写入 report
336
+ * (V3 H7 更正:旧 JSDoc 的「与 report」与冻结的 `FillTemplateReport` 矛盾)。 */
337
+ readonly dataScriptId?: string;
338
+ /** 正文槽位填充物(#118 D-3):模板含 `<!--CONTENT-->` 时必填,缺失 → `content-missing`;
339
+ * 数据页忽略本字段。内容页的 `data` 与数据页的 `content` 均为**忽略项**(互斥,见 `PAYLOAD_SLOT_RULE`)。 */
340
+ readonly content?: string;
341
+ }
342
+ export interface MarkerReport {
343
+ readonly key: TemplateMarkerKey;
344
+ readonly literal: string;
345
+ readonly rule: MarkerRule;
346
+ readonly count: number;
347
+ readonly filled: boolean;
348
+ }
349
+ /** 机器可读结果(对齐旧 CLI 结果 JSON 的 status/bytes 口径)。 */
350
+ export interface FillTemplateReport {
351
+ readonly markers: readonly MarkerReport[];
352
+ readonly strict: boolean;
353
+ /** 模板声明了 `<!--NO-SHARED-->`(SHARED 两标记允许缺席)。 */
354
+ readonly exempt: boolean;
355
+ readonly bytes: number;
356
+ }
357
+ export interface FillTemplateOutput {
358
+ readonly html: string;
359
+ readonly report: FillTemplateReport;
360
+ }
361
+ /** 冻结签名:`fillTemplate(input: FillTemplateInput): FillTemplateOutput`。 */
362
+ export type FillTemplate = (input: FillTemplateInput) => FillTemplateOutput;
363
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/spec/template.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,252 @@
1
+ /** base-paint/spec/template:占位符契约与统一填充器(#74 冻结面)。
2
+ *
3
+ * 本文件是**冻结的共享层契约类型**(type-only + 纯数据常量),与
4
+ * `src/contract.ts`(既有渲染契约 envelope→HTML)**不同物**。
5
+ *
6
+ * 命名红线(AC-1):`injector` 一词专指 sidebar 槽位装配
7
+ *(`src/injector.ts`:mountInjector/SlotsPort/openPage)。HTML 占位符
8
+ * 填充器对外名 **fillTemplate**,落点 `src/template.ts`。
9
+ * 禁止用法:htmlInjector/injectHtml/injectPlaceholders/htmlTemplateInjector。
10
+ *
11
+ * 依赖红线(AC-13):本文件只许 `import type`;base-paint 不得运行时依赖
12
+ * base-link-core(tooling/check-boundaries.mjs L15)。
13
+ */
14
+ /** 六个占位符的逐字写法(唯一真相源;文档 §3.1 是它的投影)。
15
+ *
16
+ * `content` 是 #118 收归契约的**正文槽位**(`<!--CONTENT-->`)。溯源:旧基线
17
+ * `D:/2Study/StudyNotes/SKILLS/卡路里` 73 个模板**零命中**——它不是旧 v1.30 能力,
18
+ * 而是**新架构发明的槽位**,由本仓 4 个技能(bill/chef/home/schedule)各自私有
19
+ * `CONTENT_MARKER` 事实使用(53 个内容页),#118 把它收归共享层唯一真相源。
20
+ */
21
+ export const TEMPLATE_MARKERS = Object.freeze({
22
+ injectData: '<!--INJECT-DATA-->',
23
+ content: '<!--CONTENT-->',
24
+ sharedHelpers: '<!--SHARED-HELPERS-->',
25
+ sharedCss: '<!--SHARED-CSS-->',
26
+ chartsHelpers: '<!--CHARTS-HELPERS-->',
27
+ noShared: '<!--NO-SHARED-->',
28
+ });
29
+ /** 每个标记的逐字写法与数量约束(AC-6/AC-12/#118 D-1…D-2)。
30
+ *
31
+ * `injectData` 由 `exactly-one`/`required: true` **放宽**为 `zero-or-one`/`required: false`
32
+ * ——这是 #118 授权放宽的既有签名之一(第 2 处为 `FillTemplateInput.data?`,D-8 裁定 2),理由:数据页与内容页是**互斥的两类模板**,
33
+ * 数量约束改由载荷槽规则 `PAYLOAD_SLOT_RULE` 承担(恰有其一)。
34
+ */
35
+ export const MARKER_RULES = Object.freeze({
36
+ injectData: { literal: TEMPLATE_MARKERS.injectData, rule: 'zero-or-one', required: false, exemptable: false },
37
+ content: { literal: TEMPLATE_MARKERS.content, rule: 'zero-or-one', required: false, exemptable: false },
38
+ sharedHelpers: { literal: TEMPLATE_MARKERS.sharedHelpers, rule: 'exactly-one', required: true, exemptable: true },
39
+ sharedCss: { literal: TEMPLATE_MARKERS.sharedCss, rule: 'exactly-one', required: true, exemptable: true },
40
+ chartsHelpers: { literal: TEMPLATE_MARKERS.chartsHelpers, rule: 'zero-or-one', required: false, exemptable: false },
41
+ noShared: { literal: TEMPLATE_MARKERS.noShared, rule: 'zero-or-one-exempt', required: false, exemptable: false },
42
+ });
43
+ /** 载荷槽规则(#118 D-2/A2,**机读**):`injectData`(数据页)与 `content`(内容页)**恰有其一**。
44
+ *
45
+ * - 两者都有 → `conflictCode`(`marker-conflict`);
46
+ * - 两者都无 → `missingCode`(`marker-missing`);
47
+ * - 恰有其一 → 合法(再按 `TEMPLATE_KIND_RULE` 定分型)。
48
+ *
49
+ * 事实依据:全仓 65 个模板**零冲突**(无任何一个同时含两标记,见
50
+ * `docs/research/t118-template-inventory.md` §2,**已入仓**;原 `.scratch/t118/template-inventory.md`
51
+ * 是归档前旧位置、勿据此取件);6 个数据页的 `INJECT-DATA` 全部落在
52
+ * 自带容器内,53 个内容页的 `CONTENT` 全部裸在 `:12`。
53
+ *
54
+ * 与 `TEMPLATE_CHECK_ORDER` 的关系:两槽**皆有**时由本规则报 `conflictCode`;两槽**皆无**时
55
+ * 由本规则报 `missingCode`。若同一模板还命中别的条件,取 `TEMPLATE_CHECK_ORDER` 里**更靠前**的码
56
+ * (首个命中即抛、不聚合)。
57
+ */
58
+ export const PAYLOAD_SLOT_RULE = Object.freeze({
59
+ members: ['injectData', 'content'],
60
+ rule: 'exactly-one',
61
+ conflictCode: 'marker-conflict',
62
+ missingCode: 'marker-missing',
63
+ });
64
+ /** 模板三分型(#118 D-6/A5):数据页/内容页/遗留(契约外资产)。 */
65
+ export const TEMPLATE_KINDS = ['data-page', 'content-page', 'legacy'];
66
+ /** 分型判定规则(**机读**):`required` 各恰 1 次且 `forbidden` 各 0 次即命中该型。
67
+ *
68
+ * 三型**穷尽且互斥**——但**仅对合法模板(计数 ∈ {0,1})成立**:恰有其一载荷槽 →
69
+ * `data-page`/`content-page`;两者皆无 → `legacy`;两者皆有 → 三型皆不命中,报
70
+ * `noKindCode`(三型同值,恒等于 `PAYLOAD_SLOT_RULE.conflictCode` = `marker-conflict`)。
71
+ * 计数 > 1 的模板**不属分型判定面**(本规则只对 0/1 次计数试配),由 `TEMPLATE_CHECK_ORDER`
72
+ * 的**首个命中码**报错(数量 > 1 → `marker-duplicate`,见契约 §3.1.2⑤)。
73
+ *
74
+ * `legacy` 的口径:**契约外的遗留资产**——按本规则判定的「两载荷槽皆无」,调用 `fillTemplate`
75
+ * 抛 `marker-missing` 是**正确行为**(不该能填);**payload 容器不是分型判据**(容器只由
76
+ * `CONTAINER_CHECK_RULE` 在含 `injectData` 时校验)。本仓 6 个 calorie 模板属此型,
77
+ * 处置归 #107(DESIGN D-3)。
78
+ */
79
+ export const TEMPLATE_KIND_RULE = Object.freeze({
80
+ 'data-page': { required: ['injectData'], forbidden: ['content'], noKindCode: 'marker-conflict' },
81
+ 'content-page': { required: ['content'], forbidden: ['injectData'], noKindCode: 'marker-conflict' },
82
+ legacy: { required: [], forbidden: ['injectData', 'content'], noKindCode: 'marker-conflict' },
83
+ });
84
+ /** 注入顺序(逐字对齐旧 §7「SHARED(JS) → SHARED-CSS → CHARTS → DATA」)。
85
+ *
86
+ * `content` **不在**本数组内(#118 显式记账):`INJECTION_ORDER` 是**既有冻结签名**、
87
+ * 只许追加不允许改。**正文替换与四个资产/数据步之间无先后语义**(#118 FX-6 统一口径):
88
+ * 载荷槽两成员**互斥**(`PAYLOAD_SLOT_RULE`),任一模板至多出现 `content` 与 `injectData` 之一,
89
+ * 故两者不会对同一文本竞争同一位置;`content` 既不占本数组的位置,**也不是**「恒为最后一步」。
90
+ */
91
+ export const INJECTION_ORDER = ['sharedHelpers', 'sharedCss', 'chartsHelpers', 'injectData'];
92
+ /** 共享资产的包裹约定(#118 D-5/A4,**方案 i**:资产裸文本 + 填充器负责包裹)。
93
+ *
94
+ * 两个**不变量**(违反即抛 `TemplateError`,判定谓词见 `WRAP_PREDICATES`):
95
+ * ① **资产不得自带包裹标签**——`TemplateAssets` 三个值恒为裸文本,违者报 `assetWrappedCode`;
96
+ * ② **三个共享资产标记不得被预包裹**——作用域**限定为 `ASSET_MARKER_KEYS` 的值集**
97
+ * (`sharedCss`/`sharedHelpers`/`chartsHelpers`),**排除 `injectData`**:payload 容器是
98
+ * **必需项**(`CONTAINER_CHECK_RULE`),不是「预包裹」;`content`/`noShared` 不在作用域内。
99
+ * 违者报 `markerPreWrappedCode`。
100
+ *
101
+ * 与旧基线的**偏离(显式记账)**:旧 `公共组件/README.md:63` 规定「占位符必须放在独立
102
+ * `<script>`/`<style>` 块内」(模板自带包裹),本契约**有意偏离**为填充器包裹;理由与
103
+ * 迁移代价见契约 §4.5(迁移面 12 个模板 vs 53~59 个)。parity 不受影响:两种约定产出的
104
+ * HTML 可完全一致。
105
+ */
106
+ export const ASSET_WRAP_RULE = Object.freeze({
107
+ /** 不变量①:三个资产文本不得自带包裹标签。 */
108
+ assetsBare: true,
109
+ /** 包裹标签由填充器统一写;模板与技能侧不得预包。 */
110
+ fillerWraps: true,
111
+ /** 不变量②:三个共享资产标记不得被预包裹(作用域见 `WRAP_PREDICATES`)。 */
112
+ forbidPreWrappedMarker: true,
113
+ /** 违反不变量①的错误码(资产不可用)。 */
114
+ assetWrappedCode: 'asset-missing',
115
+ /** 违反不变量②的错误码(标记与包裹约定冲突)。 */
116
+ markerPreWrappedCode: 'marker-conflict',
117
+ });
118
+ /** 三个资产的**逐字**包裹标签(唯一真相源;键与 `TemplateAssets` 对齐)。
119
+ *
120
+ * 填充器据此把裸资产包成:`sharedCssText` → `<style>` + 文本 + `</style>`;
121
+ * `sharedHelpersJs`/`chartsHelpersJs` → `<script>` + 文本 + `</script>`。
122
+ * 只许引本常量,不得在实现里另写字面量(第二真相)。
123
+ */
124
+ export const ASSET_WRAPPERS = Object.freeze({
125
+ sharedCssText: { openTag: '<style>', closeTag: '</style>' },
126
+ sharedHelpersJs: { openTag: '<script>', closeTag: '</script>' },
127
+ chartsHelpersJs: { openTag: '<script>', closeTag: '</script>' },
128
+ });
129
+ /** 资产键 → 标记键(**唯一映射**,避免「资产键」与「标记键」各写一份、漂移无从发现)。
130
+ *
131
+ * 不变量②的作用域按**标记键**表述(`WRAP_PREDICATES.forbidPreWrappedMarker.scope`),
132
+ * 而包裹标签按**资产键**表述(`ASSET_WRAPPERS`)——本常量是两者之间唯一的桥。
133
+ */
134
+ export const ASSET_MARKER_KEYS = Object.freeze({
135
+ sharedCssText: 'sharedCss',
136
+ sharedHelpersJs: 'sharedHelpers',
137
+ chartsHelpersJs: 'chartsHelpers',
138
+ });
139
+ /** 两条包裹不变量的**判定谓词**(#118 FX-3,机读;**唯一一份实现**)。
140
+ *
141
+ * 作用域(`scope`)+ 判定方式(`method`)+ 错误码(`code`)三者由本常量冻结:
142
+ *
143
+ * ① `assetsBare`(不变量①,`ASSET_WRAP_RULE.assetsBare`):`scope` = `ASSET_WRAPPERS` 的键集
144
+ * (三个资产文本)。判定方式 `trim-prefix-or-suffix` = **资产文本 `trim()` 后,以任一
145
+ * `ASSET_WRAPPERS[*].openTag` 起 或 以任一 `closeTag` 止** → 违反。
146
+ * **不得**用子串命中(`includes`):helpers JS 合法含 `<script>` 字面量(拼接 HTML),
147
+ * 子串判定会误报。仅在该资产**被消费的标记存在**时校验(豁免缺席不校验)。
148
+ * ② `forbidPreWrappedMarker`(不变量②,`ASSET_WRAP_RULE.forbidPreWrappedMarker`):
149
+ * `scope` = `ASSET_MARKER_KEYS` 的值集(三个共享资产标记),`excludes` = `['injectData']`。
150
+ * 判定方式 `enclosing-open-tag` = 对**每一个**标记出现处,扫描其**左侧全部前缀文本**
151
+ * (不限同行,故同行与跨行两种形态都命中):若最近的未闭合 `<style`/`<script` 开标签
152
+ * 存在(即 `lastIndexOf(openTag 的标签名形式) > lastIndexOf(对应 closeTag)`)→ 违反。
153
+ *
154
+ * #74 与 `tooling/classify-templates.mjs` **共用**本常量;**禁止第二份实现**(契约 §3.1.2④)。
155
+ */
156
+ export const WRAP_PREDICATES = Object.freeze({
157
+ assetsBare: {
158
+ scope: ['sharedCssText', 'sharedHelpersJs', 'chartsHelpersJs'],
159
+ method: 'trim-prefix-or-suffix',
160
+ code: 'asset-missing',
161
+ },
162
+ forbidPreWrappedMarker: {
163
+ scope: ['sharedCss', 'sharedHelpers', 'chartsHelpers'],
164
+ excludes: ['injectData'],
165
+ method: 'enclosing-open-tag',
166
+ code: 'marker-conflict',
167
+ },
168
+ });
169
+ /** payload 容器**由模板自带**,逐字形态(旧 `injector.py:119-120` 同形态):
170
+ * `<script id="payload" type="application/json"><!--INJECT-DATA--></script>`。
171
+ *
172
+ * 填充器**只把 `<!--INJECT-DATA-->` 标记文本替换为 JSON 字符串**,**不生成/不补写容器标签**
173
+ * (生成包裹会与自带容器双包)。本两个常量的用途是**校验模板自带容器**:容器存在则断言
174
+ * id/type 与 `DEFAULT_DATA_SCRIPT_ID`/`DATA_SCRIPT_TYPE`(或 `FillTemplateInput.dataScriptId`)匹配;
175
+ * 容器缺失或 id/type 不符 → 抛 `TemplateError` code `container-missing`(message 区分两因)。
176
+ */
177
+ export const DEFAULT_DATA_SCRIPT_ID = 'payload';
178
+ export const DATA_SCRIPT_TYPE = 'application/json';
179
+ /** 容器校验的作用域(#118 FX-1,机读):**仅当模板含 `appliesWhenMarker`(`injectData`)时执行**。
180
+ *
181
+ * 依据:53 个内容页**没有** payload 容器且要求模板零改动(§3.1.2④①),按无条件容器校验会
182
+ * 使 53/65 模板全抛 `container-missing`。因此条文与 `data`/`strict` 的收窄写法对齐:
183
+ * **无 `<!--INJECT-DATA-->` → 不校验容器 → 不可能抛 `container-missing`**。
184
+ * `openTag`/`closeTag` 复用 `ASSET_WRAPPERS`(容器与 JS 包裹同为一对 `<script>`/`</script>`,
185
+ * 不另写字面量);`id`/`type` 复用 `DEFAULT_DATA_SCRIPT_ID`/`DATA_SCRIPT_TYPE`。
186
+ */
187
+ export const CONTAINER_CHECK_RULE = Object.freeze({
188
+ appliesWhenMarker: 'injectData',
189
+ openTag: ASSET_WRAPPERS.sharedHelpersJs.openTag,
190
+ closeTag: ASSET_WRAPPERS.sharedHelpersJs.closeTag,
191
+ id: DEFAULT_DATA_SCRIPT_ID,
192
+ type: DATA_SCRIPT_TYPE,
193
+ code: 'container-missing',
194
+ });
195
+ /** `--strict` 时对 data 做零依赖信封校验的字段表(AC-13:base-paint 自持)。 */
196
+ export const STRICT_ENVELOPE_FIELDS = ['version', 'skill', 'shape', 'key', 'data'];
197
+ /** 与 base-link-core `ENVELOPE_SHAPES` 逐字一致,漂移由签名测试钉死。 */
198
+ export const STRICT_ENVELOPE_SHAPES = ['list', 'detail', 'stat', 'receipt', 'analysis', 'fallback'];
199
+ /** 填充器错误码(失败一律抛错,不返空页)。
200
+ *
201
+ * `data-missing` = `data === undefined` 或不可 JSON 序列化(洞 12 口径);
202
+ * `content-missing` = 模板含 `<!--CONTENT-->` 但调用方未提供 `content`(#118 D-5,与 `data-missing` 对称);
203
+ * `container-missing` = 模板含 `<!--INJECT-DATA-->` 但缺自带容器,或容器 id/type 与声明不符
204
+ * (`CONTAINER_CHECK_RULE`;**无 `INJECT-DATA` 的模板不校验容器**,FX-1)。
205
+ *
206
+ * **一码多义必须由 `message` 辨因**(#118 FX-10①,不改码):
207
+ * - `asset-missing`:① 资产为空串 ② 资产**自带包裹标签**(不变量①,`WRAP_PREDICATES.assetsBare`);
208
+ * - `marker-conflict`:① 两载荷槽皆有 ② `NO-SHARED` 与 SHARED 并存 ③ 标记被**预包裹**
209
+ * (不变量②,`WRAP_PREDICATES.forbidPreWrappedMarker`)。
210
+ *
211
+ * 只**追加**(#118 A3):既有 7 个逐字、逐序不变,`content-missing` 追加在**末尾**。
212
+ */
213
+ export const TEMPLATE_ERROR_CODES = [
214
+ 'marker-missing',
215
+ 'marker-duplicate',
216
+ 'marker-conflict',
217
+ 'data-missing',
218
+ 'container-missing',
219
+ 'asset-missing',
220
+ 'strict-invalid',
221
+ 'content-missing',
222
+ ];
223
+ /** **唯一判定次序**(#118 FX-2,机读):8 个错误码的先后,**首个命中即抛、不聚合**。
224
+ *
225
+ * 填充器按本数组顺序逐阶段判定,命中即抛对应 `code`;同一输入同时满足多个条件时,
226
+ * **只有本数组里最靠前的那个码**被抛出(不聚合、不并发报告)。
227
+ *
228
+ * 逐阶段触发条件(契约 §3.1.2⑤ 逐条对齐):
229
+ * ① `marker-duplicate`:任一标记出现 > 1 次(`MARKER_RULES` 数量规则);
230
+ * ② `marker-missing`:必需标记缺席且未豁免,或载荷槽两成员皆无(`PAYLOAD_SLOT_RULE.missingCode`);
231
+ * ③ `marker-conflict`:两载荷槽皆有(`PAYLOAD_SLOT_RULE.conflictCode`)/`NO-SHARED` 与 SHARED 并存/
232
+ * 共享资产标记被预包裹(`WRAP_PREDICATES.forbidPreWrappedMarker`);
233
+ * ④ `container-missing`:**含 `injectData` 时**缺自带容器或 id/type 不符(`CONTAINER_CHECK_RULE`);
234
+ * ⑤ `asset-missing`:被消费的资产为空串或自带包裹标签(`WRAP_PREDICATES.assetsBare`);
235
+ * ⑥ `data-missing`:数据页 `data === undefined` 或不可 JSON 序列化;
236
+ * ⑦ `content-missing`:内容页 `content === undefined`;
237
+ * ⑧ `strict-invalid`:`strict: true` 且信封校验失败。
238
+ *
239
+ * 与既有措辞的一致性:本次序使真实 calorie 模板(两载荷槽皆无 + SHARED-CSS 预包裹)
240
+ * 抛 **`marker-missing`**(② 早于 ③),与 §6.1/§3.1.2③ 的点名一致。
241
+ */
242
+ export const TEMPLATE_CHECK_ORDER = [
243
+ 'marker-duplicate',
244
+ 'marker-missing',
245
+ 'marker-conflict',
246
+ 'container-missing',
247
+ 'asset-missing',
248
+ 'data-missing',
249
+ 'content-missing',
250
+ 'strict-invalid',
251
+ ];
252
+ //# sourceMappingURL=template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/spec/template.ts"],"names":[],"mappings":"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"}