@gradial/aci 0.1.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 (64) hide show
  1. package/README.md +63 -0
  2. package/dist/astro/index.d.ts +17 -0
  3. package/dist/astro/index.js +21 -0
  4. package/dist/compiler/index.d.ts +2 -0
  5. package/dist/compiler/index.js +2 -0
  6. package/dist/compiler/validate-subset.d.ts +2 -0
  7. package/dist/compiler/validate-subset.js +73 -0
  8. package/dist/compiler/zod-to-jsonschema.d.ts +2 -0
  9. package/dist/compiler/zod-to-jsonschema.js +13 -0
  10. package/dist/content/contract.d.ts +83 -0
  11. package/dist/content/contract.js +104 -0
  12. package/dist/content/index.d.ts +6 -0
  13. package/dist/content/index.js +6 -0
  14. package/dist/content/provider.d.ts +15 -0
  15. package/dist/content/provider.js +36 -0
  16. package/dist/content/routes.d.ts +16 -0
  17. package/dist/content/routes.js +69 -0
  18. package/dist/content/tailwind-validator.d.ts +6 -0
  19. package/dist/content/tailwind-validator.js +31 -0
  20. package/dist/content/types.d.ts +105 -0
  21. package/dist/content/types.js +1 -0
  22. package/dist/content/validation.d.ts +108 -0
  23. package/dist/content/validation.js +184 -0
  24. package/dist/define-component.d.ts +2 -0
  25. package/dist/define-component.js +13 -0
  26. package/dist/define-layout.d.ts +3 -0
  27. package/dist/define-layout.js +6 -0
  28. package/dist/dev/browser.d.ts +7 -0
  29. package/dist/dev/browser.js +2 -0
  30. package/dist/dev/index.d.ts +30 -0
  31. package/dist/dev/index.js +70 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.js +3 -0
  34. package/dist/next/dev-refresh.d.ts +5 -0
  35. package/dist/next/dev-refresh.js +44 -0
  36. package/dist/next/middleware.d.ts +8 -0
  37. package/dist/next/middleware.js +132 -0
  38. package/dist/next/preview.d.ts +7 -0
  39. package/dist/next/preview.js +37 -0
  40. package/dist/next/server.d.ts +58 -0
  41. package/dist/next/server.js +269 -0
  42. package/dist/providers/file.d.ts +18 -0
  43. package/dist/providers/file.js +91 -0
  44. package/dist/sveltekit/index.d.ts +2 -0
  45. package/dist/sveltekit/index.js +18 -0
  46. package/dist/testing/index.d.ts +20 -0
  47. package/dist/testing/index.js +86 -0
  48. package/dist/types/component.d.ts +26 -0
  49. package/dist/types/component.js +1 -0
  50. package/dist/types/config.d.ts +28 -0
  51. package/dist/types/config.js +1 -0
  52. package/dist/types/index.d.ts +6 -0
  53. package/dist/types/index.js +6 -0
  54. package/dist/types/layout.d.ts +12 -0
  55. package/dist/types/layout.js +1 -0
  56. package/dist/types/page.d.ts +45 -0
  57. package/dist/types/page.js +1 -0
  58. package/dist/types/render-mode.d.ts +3 -0
  59. package/dist/types/render-mode.js +1 -0
  60. package/dist/types/renderer.d.ts +71 -0
  61. package/dist/types/renderer.js +1 -0
  62. package/package.json +112 -0
  63. package/src/cli/compile-registry.mjs +199 -0
  64. package/src/cli/verify-renderer.mjs +73 -0
@@ -0,0 +1,86 @@
1
+ import { loadRenderInput, routeMetadataForContent, } from '../content/provider.js';
2
+ import { normalizeRoute } from '../content/routes.js';
3
+ export class FixtureContentProvider {
4
+ siteConfig;
5
+ pages;
6
+ constructor(options = {}) {
7
+ this.siteConfig = cloneFixture(options.siteConfig ?? defaultFixtureSiteConfig());
8
+ this.pages = new Map();
9
+ const inputPages = options.pages ?? {
10
+ '/': defaultFixturePage(),
11
+ };
12
+ for (const [route, page] of Object.entries(inputPages)) {
13
+ if (page) {
14
+ this.pages.set(normalizeRoute(route), cloneFixture(page));
15
+ }
16
+ }
17
+ }
18
+ async loadSiteConfig() {
19
+ return cloneFixture(this.siteConfig);
20
+ }
21
+ async loadPage(route = '/') {
22
+ const page = this.pages.get(normalizeRoute(route));
23
+ return page ? cloneFixture(page) : null;
24
+ }
25
+ async listRoutes() {
26
+ return [...this.pages.keys()].sort((left, right) => left.localeCompare(right));
27
+ }
28
+ async listPublishedRoutes() {
29
+ const routes = await this.listRoutes();
30
+ const published = [];
31
+ for (const route of routes) {
32
+ const page = await this.loadPage(route);
33
+ if (page?.status === 'published') {
34
+ published.push(route);
35
+ }
36
+ }
37
+ return published;
38
+ }
39
+ async resolveRouteMetadata(route = '/') {
40
+ const [siteConfig, page] = await Promise.all([
41
+ this.loadSiteConfig(),
42
+ this.loadPage(route),
43
+ ]);
44
+ return routeMetadataForContent(siteConfig, page);
45
+ }
46
+ async loadRenderInput(route = '/', options = {}) {
47
+ return loadRenderInput(this, route, options);
48
+ }
49
+ }
50
+ export function createFixtureContentProvider(options = {}) {
51
+ return new FixtureContentProvider(options);
52
+ }
53
+ export function defaultFixtureSiteConfig() {
54
+ return {
55
+ id: 'site_fixture',
56
+ $type: 'site',
57
+ status: 'published',
58
+ title: 'Fixture Site',
59
+ domain: 'www.baremetal.local',
60
+ defaultLocale: 'en-us',
61
+ seo: {
62
+ title: 'Fixture Site',
63
+ description: 'Fixture content for SDK tests.',
64
+ siteName: 'Fixture Site',
65
+ },
66
+ };
67
+ }
68
+ export function defaultFixturePage() {
69
+ return {
70
+ id: 'home',
71
+ $type: 'page',
72
+ status: 'published',
73
+ layout: 'marketing',
74
+ renderMode: 'static',
75
+ metadata: {
76
+ title: 'Fixture Home',
77
+ description: 'Fixture home page.',
78
+ },
79
+ regions: {
80
+ main: [],
81
+ },
82
+ };
83
+ }
84
+ function cloneFixture(value) {
85
+ return JSON.parse(JSON.stringify(value));
86
+ }
@@ -0,0 +1,26 @@
1
+ import type { IslandMode, VaryDimension } from './render-mode.js';
2
+ export interface ComponentRenderModes {
3
+ canStatic: boolean;
4
+ canSSR: boolean;
5
+ canClientIsland: boolean;
6
+ }
7
+ export interface ComponentDefinition<TSchema = unknown> {
8
+ name: string;
9
+ component?: unknown;
10
+ schema: TSchema;
11
+ renderModes?: ComponentRenderModes;
12
+ render?: ComponentRenderModes;
13
+ defaultIslandMode?: IslandMode;
14
+ varyDimensions?: VaryDimension[];
15
+ vary?: VaryDimension[];
16
+ }
17
+ export interface ComponentContract<TSchema = unknown> {
18
+ name: string;
19
+ component?: unknown;
20
+ schema: TSchema;
21
+ renderModes: ComponentRenderModes;
22
+ render: ComponentRenderModes;
23
+ defaultIslandMode?: IslandMode;
24
+ varyDimensions?: VaryDimension[];
25
+ vary?: VaryDimension[];
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { RenderCapabilities } from './renderer.js';
2
+ export interface BareMetalConfig {
3
+ version: '1';
4
+ siteId: string;
5
+ framework: 'astro' | 'next' | 'sveltekit' | 'custom';
6
+ source: {
7
+ root: string;
8
+ outDir?: string;
9
+ };
10
+ componentRegistry: string;
11
+ layoutRegistry: string;
12
+ rendererEntry: string;
13
+ capabilities: RenderCapabilities;
14
+ routes: RouteConfig;
15
+ externalDependencies?: ExternalDependency[];
16
+ rendererProtocol: 'http' | 'stdio-json';
17
+ }
18
+ export interface RouteConfig {
19
+ cmsManaged: string;
20
+ frameworkOwned?: string[];
21
+ }
22
+ export interface ExternalDependency {
23
+ name: string;
24
+ host: string;
25
+ kind?: 'api' | 'client-script';
26
+ description?: string;
27
+ cacheTTL?: number;
28
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ export * from './config.js';
2
+ export * from './component.js';
3
+ export * from './layout.js';
4
+ export * from './page.js';
5
+ export * from './renderer.js';
6
+ export * from './render-mode.js';
@@ -0,0 +1,6 @@
1
+ export * from './config.js';
2
+ export * from './component.js';
3
+ export * from './layout.js';
4
+ export * from './page.js';
5
+ export * from './renderer.js';
6
+ export * from './render-mode.js';
@@ -0,0 +1,12 @@
1
+ export interface LayoutSlot {
2
+ name: string;
3
+ required: boolean;
4
+ }
5
+ export interface LayoutDefinition {
6
+ name: string;
7
+ slots: LayoutSlot[];
8
+ }
9
+ export interface LayoutContract {
10
+ name: string;
11
+ slots: LayoutSlot[];
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import type { RenderMode, VaryDimension } from './render-mode.js';
2
+ export interface PageDocument {
3
+ path: string;
4
+ layout: string;
5
+ locale: string;
6
+ metadata: PageMetadata;
7
+ regions: Record<string, RegionNode>;
8
+ renderMode: RenderMode;
9
+ }
10
+ export interface PageMetadata {
11
+ title: string;
12
+ description?: string;
13
+ openGraph?: Record<string, string>;
14
+ canonicalUrl?: string;
15
+ alternateLocales?: {
16
+ locale: string;
17
+ path: string;
18
+ }[];
19
+ customHead?: string[];
20
+ }
21
+ export interface RegionNode {
22
+ kind: 'region';
23
+ name: string;
24
+ children: Array<BlockNode | FragmentRefNode>;
25
+ }
26
+ export interface BlockNode {
27
+ kind: 'block';
28
+ id: string;
29
+ component: string;
30
+ props: Record<string, unknown>;
31
+ renderHints: BlockRenderHints;
32
+ contentRef?: string;
33
+ }
34
+ export interface BlockRenderHints {
35
+ canStatic: boolean;
36
+ needsSSR: boolean;
37
+ isIsland: boolean;
38
+ islandMode?: 'ssr' | 'client';
39
+ varyDimensions?: VaryDimension[];
40
+ }
41
+ export interface FragmentRefNode {
42
+ kind: 'fragment-ref';
43
+ fragmentId: string;
44
+ inline: boolean;
45
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export type RenderMode = 'static' | 'static-with-fragments' | 'prerender-on-demand' | 'ssr-page' | 'ssr-island' | 'client-island';
2
+ export type IslandMode = 'ssr' | 'client';
3
+ export type VaryDimension = 'geo:country' | 'geo:region' | 'geo:city' | `cookie:${string}` | `header:${string}` | 'locale' | `query:${string}`;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import type { PageDocument } from './page.js';
2
+ import type { VaryDimension } from './render-mode.js';
3
+ export interface RenderCapabilities {
4
+ staticRender: boolean;
5
+ ssr: boolean;
6
+ ssrIslands: boolean;
7
+ clientIslands: boolean;
8
+ fragmentRender: boolean;
9
+ }
10
+ export interface GradialRenderer {
11
+ renderPage(request: RenderPageRequest): Promise<RenderPageResult>;
12
+ renderFragment?(request: RenderFragmentRequest): Promise<RenderResult>;
13
+ renderIsland?(request: RenderIslandRequest): Promise<RenderResult>;
14
+ }
15
+ export interface RenderPageRequest {
16
+ page: PageDocument;
17
+ requestContext?: RequestContext;
18
+ release: ReleaseContext;
19
+ }
20
+ export interface RequestContext {
21
+ url: string;
22
+ method: string;
23
+ headers: Record<string, string>;
24
+ cookies: Record<string, string>;
25
+ geo?: GeoContext;
26
+ }
27
+ export interface GeoContext {
28
+ country?: string;
29
+ region?: string;
30
+ city?: string;
31
+ }
32
+ export interface ReleaseContext {
33
+ releaseId: string;
34
+ codeDigest: string;
35
+ contentSnapshotId: string;
36
+ assetUrl(logicalPath: string): string;
37
+ }
38
+ export interface RenderFragmentRequest {
39
+ fragmentId: string;
40
+ content: unknown;
41
+ release: ReleaseContext;
42
+ }
43
+ export interface RenderIslandRequest {
44
+ islandId: string;
45
+ component: string;
46
+ props: Record<string, unknown>;
47
+ requestContext: RequestContext;
48
+ release: ReleaseContext;
49
+ }
50
+ export interface RenderResult {
51
+ html: string;
52
+ status?: number;
53
+ headers?: Record<string, string>;
54
+ cachePolicy?: CachePolicy;
55
+ }
56
+ export interface RenderPageResult extends RenderResult {
57
+ islands?: IslandDescriptor[];
58
+ }
59
+ export interface IslandDescriptor {
60
+ islandId: string;
61
+ component: string;
62
+ props: Record<string, unknown>;
63
+ mode: 'ssr' | 'client';
64
+ varyDimensions?: VaryDimension[];
65
+ }
66
+ export interface CachePolicy {
67
+ scope: 'public' | 'private' | 'no-store';
68
+ ttl?: number;
69
+ staleWhileRevalidate?: number;
70
+ tags?: string[];
71
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "@gradial/aci",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "src/cli/*.mjs",
10
+ "README.md"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "restricted"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./compiler": {
21
+ "types": "./dist/compiler/index.d.ts",
22
+ "import": "./dist/compiler/index.js"
23
+ },
24
+ "./content": {
25
+ "types": "./dist/content/index.d.ts",
26
+ "import": "./dist/content/index.js"
27
+ },
28
+ "./content/contract": {
29
+ "types": "./dist/content/contract.d.ts",
30
+ "import": "./dist/content/contract.js"
31
+ },
32
+ "./content/validation": {
33
+ "types": "./dist/content/validation.d.ts",
34
+ "import": "./dist/content/validation.js"
35
+ },
36
+ "./content/tailwind-validator": {
37
+ "types": "./dist/content/tailwind-validator.d.ts",
38
+ "import": "./dist/content/tailwind-validator.js"
39
+ },
40
+ "./providers/file": {
41
+ "types": "./dist/providers/file.d.ts",
42
+ "import": "./dist/providers/file.js"
43
+ },
44
+ "./dev": {
45
+ "types": "./dist/dev/index.d.ts",
46
+ "import": "./dist/dev/index.js"
47
+ },
48
+ "./astro": {
49
+ "types": "./dist/astro/index.d.ts",
50
+ "import": "./dist/astro/index.js"
51
+ },
52
+ "./next/server": {
53
+ "types": "./dist/next/server.d.ts",
54
+ "import": "./dist/next/server.js",
55
+ "default": "./dist/next/server.js"
56
+ },
57
+ "./next/middleware": {
58
+ "types": "./dist/next/middleware.d.ts",
59
+ "import": "./dist/next/middleware.js",
60
+ "default": "./dist/next/middleware.js"
61
+ },
62
+ "./next/dev-refresh": {
63
+ "types": "./dist/next/dev-refresh.d.ts",
64
+ "import": "./dist/next/dev-refresh.js"
65
+ },
66
+ "./sveltekit": {
67
+ "types": "./dist/sveltekit/index.d.ts",
68
+ "import": "./dist/sveltekit/index.js"
69
+ },
70
+ "./testing": {
71
+ "types": "./dist/testing/index.d.ts",
72
+ "import": "./dist/testing/index.js"
73
+ },
74
+ "./cli/compile-registry": "./src/cli/compile-registry.mjs",
75
+ "./cli/verify-renderer": "./src/cli/verify-renderer.mjs"
76
+ },
77
+ "scripts": {
78
+ "build": "tsc -p tsconfig.json",
79
+ "compile-registry": "tsx src/cli/compile-registry.mjs",
80
+ "prepack": "npm run build"
81
+ },
82
+ "overrides": {
83
+ "postcss": "^8.5.10"
84
+ },
85
+ "peerDependencies": {
86
+ "@vercel/edge-config": "^1.4.3",
87
+ "next": "^15.5.0",
88
+ "react": "^19.0.0",
89
+ "zod": "^4.0.0"
90
+ },
91
+ "peerDependenciesMeta": {
92
+ "@vercel/edge-config": {
93
+ "optional": true
94
+ },
95
+ "next": {
96
+ "optional": true
97
+ },
98
+ "react": {
99
+ "optional": true
100
+ }
101
+ },
102
+ "devDependencies": {
103
+ "@vercel/edge-config": "^1.4.3",
104
+ "@types/node": "^24.10.1",
105
+ "@types/react": "^19.0.0",
106
+ "next": "^15.5.6",
107
+ "react": "^19.0.0",
108
+ "tsx": "^4.20.0",
109
+ "typescript": "^5.9.0",
110
+ "zod": "^4.0.0"
111
+ }
112
+ }
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { z } from 'zod';
6
+
7
+ const args = parseArgs(process.argv.slice(2));
8
+
9
+ try {
10
+ const result = await compileRegistry(args);
11
+ console.log(JSON.stringify({ success: true, ...result, errors: [] }, null, 2));
12
+ } catch (error) {
13
+ console.error(JSON.stringify({ success: false, errors: [String(error?.message ?? error)] }, null, 2));
14
+ process.exitCode = 1;
15
+ }
16
+
17
+ async function compileRegistry(options) {
18
+ const required = ['components', 'layouts', 'output'];
19
+ for (const key of required) {
20
+ if (!options[key]) throw new Error(`missing --${key}`);
21
+ }
22
+
23
+ const components = await loadDefaultArray(options.components, 'component registry');
24
+ const layouts = await loadDefaultArray(options.layouts, 'layout registry');
25
+ const outDir = path.resolve(options.output);
26
+ const schemaDir = path.join(outDir, 'schemas');
27
+ await mkdir(schemaDir, { recursive: true });
28
+
29
+ const seenComponents = new Set();
30
+ const componentEntries = [];
31
+ const schemaFiles = [];
32
+ for (const [index, component] of components.entries()) {
33
+ validateComponent(component, index, seenComponents);
34
+ const schema = compileSchema(component.schema, component.name);
35
+ const schemaFile = `schemas/${component.name}.schema.json`;
36
+ const schemaPath = path.join(outDir, schemaFile);
37
+ await writeFile(schemaPath, JSON.stringify(schema, null, 2) + '\n');
38
+ schemaFiles.push(schemaPath);
39
+ const renderModes = component.renderModes ?? component.render;
40
+ const varyDimensions = component.varyDimensions ?? component.vary;
41
+ componentEntries.push({
42
+ name: component.name,
43
+ schemaFile,
44
+ render: {
45
+ static: Boolean(renderModes.canStatic ?? renderModes.static),
46
+ ssr: Boolean(renderModes.canSSR ?? renderModes.ssr),
47
+ clientIsland: Boolean(renderModes.canClientIsland ?? renderModes.clientIsland),
48
+ },
49
+ renderModes: {
50
+ canStatic: Boolean(renderModes.canStatic ?? renderModes.static),
51
+ canSSR: Boolean(renderModes.canSSR ?? renderModes.ssr),
52
+ canClientIsland: Boolean(renderModes.canClientIsland ?? renderModes.clientIsland),
53
+ },
54
+ ...(component.defaultIslandMode ? { defaultIslandMode: component.defaultIslandMode } : {}),
55
+ ...(varyDimensions?.length ? { vary: varyDimensions, varyDimensions } : {}),
56
+ });
57
+ }
58
+
59
+ const seenLayouts = new Set();
60
+ const layoutEntries = layouts.map((layout, index) => validateLayout(layout, index, seenLayouts));
61
+
62
+ const registry = {
63
+ manifestVersion: '1',
64
+ generatedAt: new Date().toISOString(),
65
+ components: componentEntries,
66
+ layouts: layoutEntries,
67
+ };
68
+ const registryPath = path.join(outDir, 'registry.json');
69
+ await writeFile(registryPath, JSON.stringify(registry, null, 2) + '\n');
70
+ return { registry: registryPath, schemas: schemaFiles };
71
+ }
72
+
73
+ async function loadDefaultArray(modulePath, label) {
74
+ const mod = await import(pathToFileURL(path.resolve(modulePath)).href);
75
+ const value = mod.default ?? mod.registry ?? mod.components ?? mod.layouts;
76
+ if (!Array.isArray(value)) throw new Error(`${label} must export an array`);
77
+ return value;
78
+ }
79
+
80
+ function validateComponent(component, index, seen) {
81
+ if (!component || typeof component !== 'object') throw new Error(`components[${index}] must be an object`);
82
+ if (!component.name) throw new Error(`components[${index}].name is required`);
83
+ if (seen.has(component.name)) throw new Error(`duplicate component name ${component.name}`);
84
+ seen.add(component.name);
85
+ if (!component.schema) throw new Error(`components[${index}].schema is required`);
86
+ validateZodSubset(component.schema, `components[${index}].schema`);
87
+ const renderModes = component.renderModes ?? component.render;
88
+ if (!renderModes) throw new Error(`components[${index}].renderModes is required`);
89
+ const enabled = Boolean(renderModes.canStatic ?? renderModes.static)
90
+ || Boolean(renderModes.canSSR ?? renderModes.ssr)
91
+ || Boolean(renderModes.canClientIsland ?? renderModes.clientIsland);
92
+ if (!enabled) throw new Error(`components[${index}].renderModes must enable at least one mode`);
93
+ }
94
+
95
+ function validateLayout(layout, index, seen) {
96
+ if (!layout || typeof layout !== 'object') throw new Error(`layouts[${index}] must be an object`);
97
+ if (!layout.name) throw new Error(`layouts[${index}].name is required`);
98
+ if (seen.has(layout.name)) throw new Error(`duplicate layout name ${layout.name}`);
99
+ seen.add(layout.name);
100
+ if (!Array.isArray(layout.slots)) throw new Error(`layouts[${index}].slots must be an array`);
101
+ const slots = [];
102
+ const seenSlots = new Set();
103
+ for (const [slotIndex, slot] of layout.slots.entries()) {
104
+ if (!slot?.name) throw new Error(`layouts[${index}].slots[${slotIndex}].name is required`);
105
+ if (seenSlots.has(slot.name)) throw new Error(`duplicate slot name ${slot.name} in layout ${layout.name}`);
106
+ seenSlots.add(slot.name);
107
+ slots.push({ name: slot.name, required: Boolean(slot.required) });
108
+ }
109
+ return { name: layout.name, slots };
110
+ }
111
+
112
+ function compileSchema(schema, name) {
113
+ const toJSONSchema = z.toJSONSchema;
114
+ if (typeof toJSONSchema !== 'function') {
115
+ throw new Error('Zod 4 is required: install zod@^4.0.0');
116
+ }
117
+ const compiled = toJSONSchema(schema, {
118
+ target: 'draft-2020-12',
119
+ unrepresentable: 'throw',
120
+ });
121
+ return {
122
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
123
+ title: name,
124
+ ...compiled,
125
+ };
126
+ }
127
+
128
+ function validateZodSubset(schema, pathLabel, seen = new Set()) {
129
+ if (seen.has(schema)) return;
130
+ seen.add(schema);
131
+ const def = schema?._def;
132
+ if (!def) throw new Error(`${pathLabel}: expected a Zod schema`);
133
+ const typeName = def.typeName ?? def.type;
134
+ if (unsupportedZodType(typeName)) {
135
+ throw new Error(`${pathLabel}: unsupported Zod feature ${typeName}`);
136
+ }
137
+ rejectUnsupportedChecks(def, pathLabel);
138
+ for (const child of childSchemas(def)) {
139
+ validateZodSubset(child.schema, `${pathLabel}${child.path}`, seen);
140
+ }
141
+ }
142
+
143
+ function unsupportedZodType(typeName) {
144
+ return new Set([
145
+ 'ZodEffects',
146
+ 'ZodPipeline',
147
+ 'ZodLazy',
148
+ 'ZodPromise',
149
+ 'ZodFunction',
150
+ 'ZodTransform',
151
+ 'pipe',
152
+ 'lazy',
153
+ 'promise',
154
+ 'function',
155
+ 'transform',
156
+ 'custom',
157
+ ]).has(String(typeName));
158
+ }
159
+
160
+ function rejectUnsupportedChecks(def, pathLabel) {
161
+ if (!Array.isArray(def.checks)) return;
162
+ for (const [index, check] of def.checks.entries()) {
163
+ const checkType = check?.def?.type ?? check?._def?.type ?? check?._zod?.def?.check;
164
+ if (String(checkType) === 'custom') {
165
+ throw new Error(`${pathLabel}.checks[${index}]: unsupported Zod feature custom`);
166
+ }
167
+ }
168
+ }
169
+
170
+ function childSchemas(def) {
171
+ const children = [];
172
+ if (def.innerType) children.push({ path: '.inner', schema: def.innerType });
173
+ if (def.schema) children.push({ path: '.schema', schema: def.schema });
174
+ if (def.valueType) children.push({ path: '.value', schema: def.valueType });
175
+ if (def.keyType) children.push({ path: '.key', schema: def.keyType });
176
+ if (Array.isArray(def.options)) def.options.forEach((schema, index) => children.push({ path: `.options[${index}]`, schema }));
177
+ if (Array.isArray(def.items)) def.items.forEach((schema, index) => children.push({ path: `.items[${index}]`, schema }));
178
+ const shape = typeof def.shape === 'function' ? def.shape() : def.shape;
179
+ if (shape && typeof shape === 'object') {
180
+ for (const [name, schema] of Object.entries(shape)) children.push({ path: `.shape.${name}`, schema });
181
+ }
182
+ return children;
183
+ }
184
+
185
+ function parseArgs(argv) {
186
+ const out = {};
187
+ for (let i = 0; i < argv.length; i++) {
188
+ const arg = argv[i];
189
+ if (!arg.startsWith('--')) continue;
190
+ const body = arg.slice(2);
191
+ const equals = body.indexOf('=');
192
+ if (equals >= 0) {
193
+ out[body.slice(0, equals)] = body.slice(equals + 1);
194
+ continue;
195
+ }
196
+ out[body] = argv[++i];
197
+ }
198
+ return out;
199
+ }