@chat-de-hp/site 0.3.3 → 0.3.5

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/check.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import { join, relative, sep } from "node:path";
3
+ import { checkSiteConformanceSources, } from "./check-core.js";
3
4
  const SOURCE_DIRECTORY = "src";
4
5
  const SEED_PATHS = ["seed/seed.json", "seed.json"];
5
6
  const IGNORED_DIRECTORIES = new Set([
@@ -8,536 +9,19 @@ const IGNORED_DIRECTORIES = new Set([
8
9
  "dist",
9
10
  "node_modules",
10
11
  ]);
11
- /**
12
- * Reports conformance violations for one site root.
13
- *
14
- * The invariant behind the screenshot-editing fast path: a rendered CMS-backed
15
- * value is annotated, or the build fails. See
16
- * `plans/019-screenshot-site-editing.md` §7.
17
- */
18
12
  export async function checkSiteConformance(options) {
19
- const seed = await loadSeed(options.root);
20
- if (seed.fields.size === 0) {
21
- return [];
22
- }
23
- const violations = [];
24
- for (const file of await astroFiles(join(options.root, SOURCE_DIRECTORY))) {
25
- violations.push(...checkAstroSource({
26
- relativePath: toPosix(relative(options.root, file)),
27
- seed,
28
- source: await readFile(file, "utf-8"),
29
- }));
30
- }
31
- return violations.sort((left, right) => left.file.localeCompare(right.file) ||
32
- left.line - right.line ||
33
- left.field.localeCompare(right.field));
34
- }
35
- function checkAstroSource(input) {
36
- const { frontmatter, frontmatterLineOffset, template, templateLineOffset } = splitAstro(input.source);
37
- const locals = collectLocals(frontmatter, input.seed);
38
- const violations = [];
39
- const renderedFields = new Set();
40
- violations.push(...findFallbacks({
41
- code: frontmatter,
42
- lineOffset: frontmatterLineOffset,
43
- opaque: locals.opaque,
44
- relativePath: input.relativePath,
45
- seed: input.seed,
46
- }), ...findFallbacks({
47
- code: template,
48
- lineOffset: templateLineOffset,
49
- opaque: locals.opaque,
50
- relativePath: input.relativePath,
51
- seed: input.seed,
52
- }));
53
- for (const render of findRenders({ locals, seed: input.seed, template })) {
54
- renderedFields.add(`${render.collection}.${render.field}`);
55
- if (render.annotated) {
56
- continue;
57
- }
58
- violations.push({
59
- field: render.field,
60
- file: input.relativePath,
61
- line: templateLineOffset + render.line,
62
- message: `${render.collection}.${render.field} rendered without {...<entry>.edit.${render.field}}`,
63
- rule: "annotations",
64
- });
65
- }
66
- for (const rendered of renderedFields) {
67
- if (input.seed.seeded.has(rendered)) {
68
- continue;
69
- }
70
- const [, field = ""] = rendered.split(".");
71
- violations.push({
72
- field,
73
- file: input.relativePath,
74
- line: 1,
75
- message: `${rendered} rendered but absent from seed`,
76
- rule: "seed",
77
- });
78
- }
79
- return violations;
80
- }
81
- /**
82
- * Splits an `.astro` file into its frontmatter and template halves, keeping the
83
- * template's 1-based line offset so reported lines match the real file.
84
- */
85
- function splitAstro(source) {
86
- const match = /^(---\r?\n)([\s\S]*?)\r?\n---[^\n]*\r?\n?/u.exec(source);
87
- if (!match) {
88
- return {
89
- frontmatter: "",
90
- frontmatterLineOffset: 0,
91
- template: source,
92
- templateLineOffset: 0,
93
- };
94
- }
95
- const consumed = match[0];
96
- return {
97
- frontmatter: match[2] ?? "",
98
- // The opening `---` line sits above the frontmatter body.
99
- frontmatterLineOffset: countLines(match[1] ?? ""),
100
- template: source.slice(consumed.length),
101
- templateLineOffset: countLines(consumed),
102
- };
103
- }
104
- function countLines(text) {
105
- let lines = 0;
106
- for (const character of text) {
107
- if (character === "\n") {
108
- lines += 1;
109
- }
110
- }
111
- return lines;
112
- }
113
- const LEADING_MEMBER_CHAIN = /^\s*[A-Za-z_$][\w$]*((?:\s*\??\.\s*[A-Za-z_$][\w$]*)+)/u;
114
- const MEMBER_CHAIN_ONLY = /^\s*[A-Za-z_$][\w$]*(?:\s*\??\.\s*[A-Za-z_$][\w$]*)*\s*$/u;
115
- /**
116
- * Classifies frontmatter locals. `const eyebrow = info.data.hero_eyebrow`
117
- * aliases the field it carries. `const seo = getSeoMeta(post, {...})` is
118
- * opaque: its properties are derived values, so `seo.title` colliding with a
119
- * field slug is not a CMS read. A local that only *selects by* a field
120
- * (`items.find((i) => i.is_featured)`) is neither — it carries an entry, not
121
- * that field's value.
122
- */
123
- function collectLocals(frontmatter, seed) {
124
- const aliases = new Map();
125
- const opaque = new Set();
126
- const declaration = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([^;\n]+)/gu;
127
- let match = declaration.exec(frontmatter);
128
- while (match !== null) {
129
- const name = match[1] ?? "";
130
- const initializer = match[2] ?? "";
131
- if (!(aliases.has(name) || opaque.has(name))) {
132
- const field = leadingChainField(initializer, seed);
133
- if (field) {
134
- aliases.set(name, field);
135
- }
136
- else if (!(
137
- // `const data = info.data` re-roots an entry, not a derived value.
138
- (MEMBER_CHAIN_ONLY.test(initializer) ||
139
- // Awaited initializers are entry/settings fetches; their fields count.
140
- /\bawait\b/u.test(initializer) ||
141
- firstFieldRead(initializer, seed)))) {
142
- opaque.add(name);
143
- }
144
- }
145
- match = declaration.exec(frontmatter);
146
- }
147
- return { aliases, opaque };
148
- }
149
- /**
150
- * The CMS field read by `expression`'s leading member chain —
151
- * `info.data.hero_title.split("…")` reads `hero_title`, while
152
- * `items.find((i) => i.hero_title)` reads none: a field inside call arguments
153
- * selects, it does not flow into the local.
154
- */
155
- function leadingChainField(expression, seed) {
156
- const chain = LEADING_MEMBER_CHAIN.exec(expression)?.[1] ?? "";
157
- return firstFieldRead(chain, seed);
158
- }
159
- /**
160
- * The root identifier of the member chain containing the property at `index`,
161
- * or null when the chain hangs off something other than a plain identifier
162
- * (`getEntry(x).title`, `rows[0].name`).
163
- */
164
- function chainRootAt(code, index) {
165
- let cursor = index - 1;
166
- for (;;) {
167
- while (cursor >= 0 && /\s/u.test(code[cursor] ?? "")) {
168
- cursor -= 1;
169
- }
170
- if (code[cursor] !== ".") {
171
- return null;
172
- }
173
- cursor -= 1;
174
- if (code[cursor] === "?") {
175
- cursor -= 1;
176
- }
177
- while (cursor >= 0 && /\s/u.test(code[cursor] ?? "")) {
178
- cursor -= 1;
179
- }
180
- const end = cursor;
181
- while (cursor >= 0 && /[\w$]/u.test(code[cursor] ?? "")) {
182
- cursor -= 1;
183
- }
184
- if (cursor === end) {
185
- return null;
186
- }
187
- const name = code.slice(cursor + 1, end + 1);
188
- let before = cursor;
189
- while (before >= 0 && /\s/u.test(code[before] ?? "")) {
190
- before -= 1;
191
- }
192
- if (code[before] !== ".") {
193
- return name;
194
- }
195
- cursor = before;
196
- }
197
- }
198
- /** The first CMS field slug read by a property access in `expression`. */
199
- function firstFieldRead(expression, seed) {
200
- for (const access of propertyAccesses(expression)) {
201
- if (seed.fields.has(access.property)) {
202
- return access.property;
203
- }
204
- }
205
- return null;
206
- }
207
- /** Every `<expr>.<name>` / `<expr>?.<name>` property read in `code`. */
208
- function propertyAccesses(code) {
209
- const accesses = [];
210
- const pattern = /\??\.\s*([A-Za-z_$][\w$]*)/gu;
211
- let match = pattern.exec(code);
212
- while (match !== null) {
213
- accesses.push({
214
- index: match.index + match[0].length - (match[1]?.length ?? 0),
215
- property: match[1] ?? "",
216
- });
217
- match = pattern.exec(code);
218
- }
219
- return accesses;
220
- }
221
- function findFallbacks(input) {
222
- const violations = [];
223
- // `<something>.<cms field> ?? <default>` — the default belongs in seed data.
224
- const pattern = /\??\.\s*([A-Za-z_$][\w$]*)\s*\?\?/gu;
225
- let match = pattern.exec(input.code);
226
- while (match !== null) {
227
- const field = match[1] ?? "";
228
- const collections = input.seed.fields.get(field);
229
- const root = chainRootAt(input.code, input.code.indexOf(field, match.index));
230
- if (collections?.[0] && !(root && input.opaque.has(root))) {
231
- violations.push({
232
- field,
233
- file: input.relativePath,
234
- line: input.lineOffset + lineAt(input.code, match.index),
235
- message: `${collections[0].collection}.${field} has a hardcoded ?? fallback; defaults belong in seed data`,
236
- rule: "fallbacks",
237
- });
238
- }
239
- match = pattern.exec(input.code);
240
- }
241
- return violations;
242
- }
243
- /**
244
- * Finds each CMS-backed value rendered by the template and whether an
245
- * annotation covers it — either on the element it renders in or on an ancestor
246
- * wrapper, which is how a transformed value stays one target.
247
- *
248
- * Guard reads (`{price && …}`) only test the value, so like plain HTML
249
- * attributes they are not renders; the render a guard protects is checked on
250
- * its own. A genuinely unannotated render is always reported, even when the
251
- * same field is annotated elsewhere in the file: each render site has to carry
252
- * its own annotation for the routing rule to be honest about that element.
253
- */
254
- function findRenders(input) {
255
- const annotations = findAnnotations(input.template);
256
- const renders = [];
257
- const seen = new Set();
258
- for (const expression of templateExpressions(input.template)) {
259
- for (const read of fieldsRenderedBy(expression.code, input)) {
260
- const collections = input.seed.fields.get(read.field);
261
- const collection = collections?.[0]?.collection;
262
- if (!collection || isGuardRead(expression.code, read.index)) {
263
- continue;
264
- }
265
- // Offsets are absolute so an annotation on an ancestor covers the read.
266
- const index = expression.index + read.index;
267
- const line = lineAt(input.template, index);
268
- const key = `${collection}.${read.field}:${line}`;
269
- if (seen.has(key)) {
270
- continue;
271
- }
272
- seen.add(key);
273
- renders.push({
274
- annotated: isAnnotated({ annotations, field: read.field, index }),
275
- collection,
276
- field: read.field,
277
- line,
278
- });
279
- }
280
- }
281
- return renders;
282
- }
283
- /**
284
- * Whether the read at `index` only tests its value. True when what follows is a
285
- * boolean operator or a ternary condition rather than the end of the
286
- * expression, as in `{price && (…)}` or `{price ? … : …}`.
287
- *
288
- * Trailing property access is part of the read being tested, so
289
- * `{image?.id && (…)}` is a guard on `image` just as `{image && (…)}` is.
290
- */
291
- function isGuardRead(code, index) {
292
- const rest = code
293
- .slice(index)
294
- .replace(/^[\w$]+/u, "")
295
- .replace(/^(\??\.\s*[\w$]+)*\s*/u, "");
296
- return /^(&&|\|\||\?[^.?])/u.test(rest);
297
- }
298
- /**
299
- * CMS fields a rendered expression resolves to, directly or via an alias, each
300
- * with the offset it is read at.
301
- */
302
- function fieldsRenderedBy(code, input) {
303
- const reads = [];
304
- for (const access of propertyAccesses(code)) {
305
- if (!input.seed.fields.has(access.property)) {
306
- continue;
307
- }
308
- const root = chainRootAt(code, access.index);
309
- if (root && input.locals.opaque.has(root)) {
310
- continue;
311
- }
312
- reads.push({ field: access.property, index: access.index });
313
- }
314
- for (const identifier of bareIdentifiers(code)) {
315
- const aliased = input.locals.aliases.get(identifier.name);
316
- if (aliased) {
317
- reads.push({ field: aliased, index: identifier.index });
318
- }
319
- }
320
- return reads;
321
- }
322
- /** Identifiers used on their own — not as `.property` and not as a call. */
323
- function bareIdentifiers(code) {
324
- const identifiers = [];
325
- const pattern = /(^|[^.\w$?])([A-Za-z_$][\w$]*)\s*(?![\w$(.])/gu;
326
- let match = pattern.exec(code);
327
- while (match !== null) {
328
- identifiers.push({
329
- index: match.index + (match[1]?.length ?? 0),
330
- name: match[2] ?? "",
331
- });
332
- match = pattern.exec(code);
333
- }
334
- return identifiers;
335
- }
336
- /**
337
- * Locates `{...<entry>.edit.<field>}` spreads and the span of the element each
338
- * one annotates, so a wrapper annotation covers everything it contains.
339
- */
340
- function findAnnotations(template) {
341
- const annotations = [];
342
- const pattern = /\{\s*\.\.\.\s*[\w$?.]*\bedit\??\.\s*([A-Za-z_$][\w$]*)\s*\}/gu;
343
- let match = pattern.exec(template);
344
- while (match !== null) {
345
- const start = template.lastIndexOf("<", match.index);
346
- if (start !== -1) {
347
- annotations.push({
348
- end: elementEnd(template, start),
349
- field: match[1] ?? "",
350
- start,
351
- });
352
- }
353
- match = pattern.exec(template);
354
- }
355
- return annotations;
356
- }
357
- /**
358
- * End offset of the element opening at `start`: the matching close tag when the
359
- * element has one, otherwise the end of its self-closing/void tag.
360
- */
361
- function elementEnd(template, start) {
362
- const tagMatch = /^<\s*([A-Za-z][\w:.-]*)/u.exec(template.slice(start));
363
- const tag = tagMatch?.[1];
364
- const openTagEnd = template.indexOf(">", start);
365
- if (!tag || openTagEnd === -1) {
366
- return template.length;
367
- }
368
- if (template[openTagEnd - 1] === "/") {
369
- return openTagEnd + 1;
370
- }
371
- const boundary = new RegExp(`<\\s*(/)?${escapeRegExp(tag)}(?=[\\s/>])`, "gu");
372
- boundary.lastIndex = openTagEnd + 1;
373
- let depth = 1;
374
- let match = boundary.exec(template);
375
- while (match !== null) {
376
- depth += match[1] ? -1 : 1;
377
- if (depth === 0) {
378
- return match.index + match[0].length;
379
- }
380
- match = boundary.exec(template);
381
- }
382
- return template.length;
383
- }
384
- function escapeRegExp(value) {
385
- return value.replaceAll(/[.*+?^${}()|[\]\\-]/gu, String.raw `\$&`);
386
- }
387
- function isAnnotated(input) {
388
- return input.annotations.some((annotation) => annotation.field === input.field &&
389
- input.index >= annotation.start &&
390
- input.index < annotation.end);
13
+ return checkSiteConformanceSources({
14
+ files: astroSources(options.root),
15
+ seed: await readSeedSource(options.root),
16
+ });
391
17
  }
392
- /**
393
- * `{...}` expressions in template position, including those nested inside
394
- * markup an outer expression returns, and those passed as props to a component.
395
- * Annotation spreads are skipped: only what the page actually renders counts.
396
- *
397
- * Nested expressions are visited separately rather than as part of their
398
- * parent's text, so an identifier is only read where it is genuinely
399
- * evaluated — an `aria-label` attribute is markup, not a render of `label`.
400
- *
401
- * A component prop (`<Image image={data.hero_image} />`) renders its value, so
402
- * it counts. A plain HTML attribute (`href={mapUrl}`) does not: it carries
403
- * metadata rather than visible content the customer would screenshot.
404
- */
405
- function templateExpressions(template, offset = 0) {
406
- const expressions = [];
407
- let index = 0;
408
- let componentTagDepth = 0;
409
- let insideTag = false;
410
- while (index < template.length) {
411
- const character = template[index];
412
- if (character === "<" && /[A-Za-z/]/u.test(template[index + 1] ?? "")) {
413
- insideTag = true;
414
- // Components are capitalized; lowercase names are HTML elements.
415
- componentTagDepth = /^<\s*[A-Z]/u.test(template.slice(index, index + 3))
416
- ? 1
417
- : 0;
418
- index += 1;
419
- continue;
420
- }
421
- if (character === ">" && insideTag) {
422
- insideTag = false;
423
- componentTagDepth = 0;
424
- index += 1;
425
- continue;
426
- }
427
- if (character !== "{") {
428
- index += 1;
429
- continue;
430
- }
431
- if (insideTag) {
432
- if (componentTagDepth > 0) {
433
- const attributeEnd = matchingBrace(template, index);
434
- const attributeBody = template.slice(index + 1, attributeEnd);
435
- if (!isSpread(attributeBody)) {
436
- expressions.push({
437
- code: attributeBody,
438
- index: offset + index + 1,
439
- });
440
- }
441
- index = attributeEnd + 1;
442
- continue;
443
- }
444
- index += 1;
445
- continue;
446
- }
447
- const end = matchingBrace(template, index);
448
- const body = template.slice(index + 1, end);
449
- if (!isSpread(body)) {
450
- const bodyStart = offset + index + 1;
451
- const markupAt = body.search(/<\s*[A-Za-z]/u);
452
- if (markupAt === -1) {
453
- expressions.push({ code: body, index: bodyStart });
454
- }
455
- else {
456
- // Everything before the markup is this expression's own code; the
457
- // markup is a nested template scanned on its own terms.
458
- expressions.push({
459
- code: body.slice(0, markupAt),
460
- index: bodyStart,
461
- });
462
- expressions.push(...templateExpressions(body.slice(markupAt), bodyStart + markupAt));
463
- }
464
- }
465
- index = end + 1;
466
- }
467
- return expressions;
468
- }
469
- function isSpread(code) {
470
- return code.trimStart().startsWith("...");
471
- }
472
- function matchingBrace(template, start) {
473
- let depth = 0;
474
- for (let index = start; index < template.length; index += 1) {
475
- const character = template[index];
476
- if (character === "{") {
477
- depth += 1;
478
- }
479
- else if (character === "}") {
480
- depth -= 1;
481
- if (depth === 0) {
482
- return index;
483
- }
484
- }
485
- }
486
- return template.length;
487
- }
488
- function lineAt(code, index) {
489
- return countLines(code.slice(0, index)) + 1;
490
- }
491
- async function loadSeed(root) {
492
- const raw = await readSeedFile(root);
493
- const parsed = JSON.parse(raw);
494
- const fields = new Map();
495
- const seeded = new Set();
496
- if (!isRecord(parsed)) {
497
- return { fields, seeded };
498
- }
499
- const collections = Array.isArray(parsed.collections)
500
- ? parsed.collections
501
- : [];
502
- for (const collection of collections) {
503
- if (!(isRecord(collection) && typeof collection.slug === "string")) {
504
- continue;
505
- }
506
- const collectionSlug = collection.slug;
507
- const collectionFields = Array.isArray(collection.fields)
508
- ? collection.fields
509
- : [];
510
- for (const field of collectionFields) {
511
- if (!(isRecord(field) && typeof field.slug === "string")) {
512
- continue;
513
- }
514
- const entries = fields.get(field.slug) ?? [];
515
- entries.push({ collection: collectionSlug, slug: field.slug });
516
- fields.set(field.slug, entries);
517
- }
518
- }
519
- const content = isRecord(parsed.content) ? parsed.content : {};
520
- for (const [collectionSlug, entries] of Object.entries(content)) {
521
- if (!Array.isArray(entries)) {
522
- continue;
523
- }
524
- for (const entry of entries) {
525
- if (!(isRecord(entry) && isRecord(entry.data))) {
526
- continue;
527
- }
528
- for (const [slug, value] of Object.entries(entry.data)) {
529
- if (value !== null && value !== undefined && value !== "") {
530
- seeded.add(`${collectionSlug}.${slug}`);
531
- }
532
- }
533
- }
534
- }
535
- return { fields, seeded };
536
- }
537
- async function readSeedFile(root) {
18
+ async function readSeedSource(root) {
538
19
  for (const candidate of SEED_PATHS) {
539
20
  try {
540
- return await readFile(join(root, candidate), "utf-8");
21
+ return {
22
+ file: candidate,
23
+ source: await readFile(join(root, candidate), "utf-8"),
24
+ };
541
25
  }
542
26
  catch {
543
27
  // Try the next supported seed location.
@@ -545,6 +29,14 @@ async function readSeedFile(root) {
545
29
  }
546
30
  throw new Error(`Could not find seed data in ${root}. Expected ${SEED_PATHS.join(" or ")}.`);
547
31
  }
32
+ async function* astroSources(root) {
33
+ for (const file of await astroFiles(join(root, SOURCE_DIRECTORY))) {
34
+ yield {
35
+ file: toPosix(relative(root, file)),
36
+ source: await readFile(file, "utf-8"),
37
+ };
38
+ }
39
+ }
548
40
  async function astroFiles(directory) {
549
41
  let entries;
550
42
  try {
@@ -568,9 +60,6 @@ async function astroFiles(directory) {
568
60
  }
569
61
  return files;
570
62
  }
571
- function isRecord(value) {
572
- return typeof value === "object" && value !== null && !Array.isArray(value);
573
- }
574
63
  function toPosix(path) {
575
64
  return sep === "/" ? path : path.split(sep).join("/");
576
65
  }
@@ -1,10 +1,10 @@
1
- import { z } from "zod";
1
+ import { type infer as InferSchema } from "zod/mini";
2
2
  export declare const SITE_RUNTIME_NAME: "@chat-de-hp/site";
3
3
  export declare const SITE_RUNTIME_VERSION: string;
4
4
  export declare const SITE_RUNTIME_PROTOCOL_VERSION: 1;
5
- export declare const siteRuntimeBindingSchema: z.ZodObject<{
6
- name: z.ZodString;
7
- type: z.ZodEnum<{
5
+ export declare const siteRuntimeBindingSchema: import("zod/mini").ZodMiniObject<{
6
+ name: import("zod/mini").ZodMiniString<string>;
7
+ type: import("zod/mini").ZodMiniEnum<{
8
8
  d1: "d1";
9
9
  images: "images";
10
10
  kv_namespace: "kv_namespace";
@@ -12,20 +12,20 @@ export declare const siteRuntimeBindingSchema: z.ZodObject<{
12
12
  send_email: "send_email";
13
13
  worker_loader: "worker_loader";
14
14
  }>;
15
- }, z.core.$loose>;
16
- export declare const siteRuntimeManifestSchema: z.ZodObject<{
17
- capabilities: z.ZodArray<z.ZodObject<{
18
- id: z.ZodString;
19
- provider: z.ZodString;
20
- schemaVersion: z.ZodNumber;
21
- }, z.core.$loose>>;
22
- primitives: z.ZodArray<z.ZodObject<{
23
- id: z.ZodString;
24
- schemaVersion: z.ZodNumber;
25
- }, z.core.$loose>>;
26
- requiredBindings: z.ZodArray<z.ZodObject<{
27
- name: z.ZodString;
28
- type: z.ZodEnum<{
15
+ }, import("zod/v4/core").$loose>;
16
+ export declare const siteRuntimeManifestSchema: import("zod/mini").ZodMiniObject<{
17
+ capabilities: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
18
+ id: import("zod/mini").ZodMiniString<string>;
19
+ provider: import("zod/mini").ZodMiniString<string>;
20
+ schemaVersion: import("zod/mini").ZodMiniNumber<number>;
21
+ }, import("zod/v4/core").$loose>>;
22
+ primitives: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
23
+ id: import("zod/mini").ZodMiniString<string>;
24
+ schemaVersion: import("zod/mini").ZodMiniNumber<number>;
25
+ }, import("zod/v4/core").$loose>>;
26
+ requiredBindings: import("zod/mini").ZodMiniArray<import("zod/mini").ZodMiniObject<{
27
+ name: import("zod/mini").ZodMiniString<string>;
28
+ type: import("zod/mini").ZodMiniEnum<{
29
29
  d1: "d1";
30
30
  images: "images";
31
31
  kv_namespace: "kv_namespace";
@@ -33,15 +33,15 @@ export declare const siteRuntimeManifestSchema: z.ZodObject<{
33
33
  send_email: "send_email";
34
34
  worker_loader: "worker_loader";
35
35
  }>;
36
- }, z.core.$loose>>;
37
- runtime: z.ZodObject<{
38
- name: z.ZodLiteral<"@chat-de-hp/site">;
39
- protocolVersion: z.ZodLiteral<1>;
40
- version: z.ZodString;
41
- }, z.core.$loose>;
42
- schemaVersion: z.ZodLiteral<1>;
43
- }, z.core.$loose>;
44
- export type SiteRuntimeBinding = z.infer<typeof siteRuntimeBindingSchema>;
45
- export type SiteRuntimeManifest = z.infer<typeof siteRuntimeManifestSchema>;
36
+ }, import("zod/v4/core").$loose>>;
37
+ runtime: import("zod/mini").ZodMiniObject<{
38
+ name: import("zod/mini").ZodMiniLiteral<"@chat-de-hp/site">;
39
+ protocolVersion: import("zod/mini").ZodMiniLiteral<1>;
40
+ version: import("zod/mini").ZodMiniString<string>;
41
+ }, import("zod/v4/core").$loose>;
42
+ schemaVersion: import("zod/mini").ZodMiniLiteral<1>;
43
+ }, import("zod/v4/core").$loose>;
44
+ export type SiteRuntimeBinding = InferSchema<typeof siteRuntimeBindingSchema>;
45
+ export type SiteRuntimeManifest = InferSchema<typeof siteRuntimeManifestSchema>;
46
46
  export declare function parseSiteRuntimeManifest(value: unknown): SiteRuntimeManifest;
47
47
  //# sourceMappingURL=control-plane.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,iBAAiB,EAAG,kBAA2B,CAAC;AAC7D,eAAO,MAAM,oBAAoB,QAAsB,CAAC;AACxD,eAAO,MAAM,6BAA6B,EAAG,CAAU,CAAC;AAExD,eAAO,MAAM,wBAAwB;;;;;;;;;;iBAYrB,CAAC;AAEjB,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BtB,CAAC;AAEjB,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAE5E"}
1
+ {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,KAAK,IAAI,WAAW,EAQ1B,MAAM,UAAU,CAAC;AAIlB,eAAO,MAAM,iBAAiB,EAAG,kBAA2B,CAAC;AAC7D,eAAO,MAAM,oBAAoB,QAAsB,CAAC;AACxD,eAAO,MAAM,6BAA6B,EAAG,CAAU,CAAC;AAExD,eAAO,MAAM,wBAAwB;;;;;;;;;;gCAUnC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAqBpC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC9E,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAEhF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAQ5E"}