@ggui-ai/project-config 0.1.0-rc.1

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.
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Primitive manifest discovery — turns a loaded `ggui.json` into the
3
+ * set of primitive catalogs the project declares via
4
+ * `primitives.packages` (npm specifiers) and `primitives.local`
5
+ * (globs).
6
+ *
7
+ * The flow:
8
+ *
9
+ * ggui.json.primitives.packages (npm specifiers)
10
+ * → resolve each spec through Node's module resolution
11
+ * → walk up to the enclosing `package.json`
12
+ * → read `ggui.primitives.json` alongside it
13
+ * → parse via {@link parsePrimitivesManifest}
14
+ *
15
+ * ggui.json.primitives.local (globs relative to project root)
16
+ * → filesystem match (tinyglobby, same options as blueprint
17
+ * discovery)
18
+ * → filter to basename === `ggui.primitives.json`
19
+ * → parse via {@link parsePrimitivesManifest}
20
+ *
21
+ * For npm specifiers, the resolver walks UP from the resolved subpath
22
+ * file to find the enclosing `package.json` — this avoids requiring
23
+ * packages to add `ggui.primitives.json` to their `exports` block and
24
+ * matches the intuition that "the manifest lives at the package root".
25
+ *
26
+ * ## Error model
27
+ *
28
+ * Discovery never throws on a per-source failure. One malformed
29
+ * manifest should not black-hole the rest of the project — the
30
+ * `ggui dev` hub renders the valid catalogs + flags the bad ones in
31
+ * the UI. Each issue carries a path + message so callers can render
32
+ * a useful list.
33
+ *
34
+ * Callers that need fail-fast semantics (e.g. `ggui serve` boot,
35
+ * where continuing with a partial set is a silent deploy-time
36
+ * regression) inspect `result.issues` themselves and escalate to a
37
+ * thrown error when non-empty. This helper stays policy-neutral —
38
+ * mirrors the blueprint-discovery split.
39
+ *
40
+ * ## Why `@ggui-ai/project-config/node`, not a sibling package
41
+ *
42
+ * Discovery is filesystem + parse + identity check. It is NOT HTTP,
43
+ * agent runtime, or generator state. Consumers today:
44
+ *
45
+ * - `@ggui-ai/mcp-server` at `ggui serve` boot time — builds the
46
+ * primitive index so future generator consumers can enumerate
47
+ * every declared source.
48
+ * - `@ggui-ai/dev-stack` may consume it later for a unified
49
+ * `ggui dev` hub view (symmetric with blueprint discovery).
50
+ *
51
+ * Housing it here keeps one source of truth and avoids an inbound
52
+ * dep on either consumer.
53
+ */
54
+ import { existsSync, readFileSync } from 'node:fs';
55
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
56
+ import { fileURLToPath, pathToFileURL } from 'node:url';
57
+ import { glob } from 'tinyglobby';
58
+ import { GGUI_PRIMITIVES_JSON_FILENAME, parsePrimitivesManifest, } from './primitives-manifest.js';
59
+ /**
60
+ * Walk both `primitives.packages` and `primitives.local`, build the
61
+ * catalog, accumulate issues.
62
+ *
63
+ * Non-throwing per source. Duplicate-`import`-specifier across
64
+ * sources is an issue (first-seen wins, later ones accumulate as
65
+ * issues). A glob pattern that matches nothing is allowed — the
66
+ * manifest may exist ahead of primitives being authored; operators
67
+ * can still run `ggui dev` without the catalog being complete.
68
+ */
69
+ export async function discoverPrimitives(options) {
70
+ const { projectRoot, manifest } = options;
71
+ if (!isAbsolute(projectRoot)) {
72
+ throw new Error(`discoverPrimitives: projectRoot must be absolute, got ${projectRoot}`);
73
+ }
74
+ const catalogs = [];
75
+ const issues = [];
76
+ const seenImports = new Map(); // import → manifestPath
77
+ const resolveModule = options.resolveModule ?? makeDefaultModuleResolver(projectRoot);
78
+ // ── packages: npm specifiers ────────────────────────────────────
79
+ for (const spec of manifest.primitives.packages) {
80
+ let entryPath;
81
+ try {
82
+ entryPath = resolveModule(spec, projectRoot);
83
+ }
84
+ catch (cause) {
85
+ issues.push({
86
+ path: spec,
87
+ message: `Could not resolve primitive package "${spec}" — is it installed in the project? ` +
88
+ `(${errorMessage(cause)})`,
89
+ cause,
90
+ });
91
+ continue;
92
+ }
93
+ const pkgRoot = findEnclosingPackageRoot(entryPath);
94
+ if (!pkgRoot) {
95
+ issues.push({
96
+ path: spec,
97
+ message: `Primitive package "${spec}" resolved to ${entryPath}, but no enclosing package.json was found.`,
98
+ });
99
+ continue;
100
+ }
101
+ const manifestPath = join(pkgRoot, GGUI_PRIMITIVES_JSON_FILENAME);
102
+ if (!existsSync(manifestPath)) {
103
+ issues.push({
104
+ path: spec,
105
+ message: `Primitive package "${spec}" is missing ${GGUI_PRIMITIVES_JSON_FILENAME} at its root (${pkgRoot}). ` +
106
+ `Expected convention: one ggui.primitives.json alongside package.json.`,
107
+ });
108
+ continue;
109
+ }
110
+ const parsed = loadManifestAt(manifestPath, spec);
111
+ if (!parsed.ok) {
112
+ issues.push(parsed.issue);
113
+ continue;
114
+ }
115
+ // The declared `ggui.json` spec MUST match the manifest's own
116
+ // `import` field. A mismatch means the operator declared the
117
+ // wrong package name OR the package is advertising a different
118
+ // specifier — either way, the generator's import-line would be
119
+ // wrong. Surface it here rather than hand the agent a broken
120
+ // catalog.
121
+ if (parsed.manifest.import !== spec) {
122
+ issues.push({
123
+ path: spec,
124
+ message: `Primitive package "${spec}" declares import="${parsed.manifest.import}" in its ` +
125
+ `ggui.primitives.json, but ggui.json lists it under primitives.packages as "${spec}". ` +
126
+ `These must match — either update ggui.json's entry or the package's manifest.`,
127
+ });
128
+ continue;
129
+ }
130
+ recordCatalog(catalogs, issues, seenImports, {
131
+ source: 'package',
132
+ import: parsed.manifest.import,
133
+ manifestPath,
134
+ manifest: parsed.manifest,
135
+ });
136
+ }
137
+ // ── local: globs ────────────────────────────────────────────────
138
+ const localPatterns = manifest.primitives.local;
139
+ if (localPatterns.length > 0) {
140
+ const matches = await glob(localPatterns, {
141
+ cwd: projectRoot,
142
+ absolute: true,
143
+ onlyFiles: true,
144
+ followSymbolicLinks: false,
145
+ dot: false,
146
+ });
147
+ for (const absolutePath of matches) {
148
+ const rel = relative(projectRoot, absolutePath);
149
+ const displayPath = rel === '' ? absolutePath : rel;
150
+ // Mirror blueprint discovery's wide-`**` safety — skip matches
151
+ // whose basename isn't the canonical filename. Users sometimes
152
+ // write `ui/**/*.json` patterns and pick up arbitrary JSON.
153
+ if (!absolutePath.endsWith(sep + GGUI_PRIMITIVES_JSON_FILENAME)) {
154
+ continue;
155
+ }
156
+ const parsed = loadManifestAt(absolutePath, displayPath);
157
+ if (!parsed.ok) {
158
+ issues.push(parsed.issue);
159
+ continue;
160
+ }
161
+ recordCatalog(catalogs, issues, seenImports, {
162
+ source: 'local',
163
+ import: parsed.manifest.import,
164
+ manifestPath: absolutePath,
165
+ manifest: parsed.manifest,
166
+ });
167
+ }
168
+ }
169
+ return { catalogs, issues };
170
+ }
171
+ /**
172
+ * Convenience wrapper for the common "I have a `ggui.json` path,
173
+ * give me the primitives" flow. Resolves the project root from the
174
+ * manifest path.
175
+ */
176
+ export async function discoverPrimitivesFromGguiJsonPath(manifestPath, manifest, extra = {}) {
177
+ const projectRoot = dirname(resolve(manifestPath));
178
+ return discoverPrimitives({ projectRoot, manifest, ...extra });
179
+ }
180
+ // ─────────────────────────────────────────────────────────────────────
181
+ // Internals
182
+ // ─────────────────────────────────────────────────────────────────────
183
+ function makeDefaultModuleResolver(projectRoot) {
184
+ // Anchor at `<projectRoot>/package.json` — the file doesn't have
185
+ // to exist for resolution to produce a working answer; Node uses
186
+ // the path only to establish the ancestor `node_modules/` chain.
187
+ //
188
+ // We use `import.meta.resolve` rather than `createRequire().resolve`
189
+ // because primitive packages (`@ggui-ai/design`, sibling catalogs
190
+ // operators may author) are ESM-only — their `package.json#exports`
191
+ // declare only the `"import"` condition per the open-source
192
+ // subtree's `"type": "module"` convention. CommonJS `require.resolve`
193
+ // cannot satisfy a subpath whose `exports` entry has no `"require"`
194
+ // condition, even though the subpath IS defined for ESM consumers —
195
+ // the resolver throws `ERR_PACKAGE_PATH_NOT_EXPORTED` with a
196
+ // misleading "not defined by exports" message.
197
+ //
198
+ // Crucially, discovery never actually `require()`s the resolved
199
+ // entry; it only walks UP from the entry path to find the enclosing
200
+ // `package.json` (see `findEnclosingPackageRoot` above). So using
201
+ // an ESM-native resolver is both honest (matches the package's
202
+ // actual condition set) and safe (no downstream CJS load).
203
+ //
204
+ // `import.meta.resolve(spec, parent)` is stable + synchronous in
205
+ // Node 20+. The returned URL is converted back to a filesystem path
206
+ // with `fileURLToPath` so the rest of the pipeline stays
207
+ // path-based.
208
+ const originUrl = pathToFileURL(join(projectRoot, 'package.json')).href;
209
+ return (specifier) => fileURLToPath(import.meta.resolve(specifier, originUrl));
210
+ }
211
+ function findEnclosingPackageRoot(startFile) {
212
+ let dir = dirname(resolve(startFile));
213
+ for (;;) {
214
+ if (existsSync(join(dir, 'package.json')))
215
+ return dir;
216
+ const parent = dirname(dir);
217
+ if (parent === dir)
218
+ return null;
219
+ dir = parent;
220
+ }
221
+ }
222
+ function loadManifestAt(absolutePath, displayPath) {
223
+ let raw;
224
+ try {
225
+ raw = readFileSync(absolutePath, 'utf-8');
226
+ }
227
+ catch (cause) {
228
+ return {
229
+ ok: false,
230
+ issue: {
231
+ path: displayPath,
232
+ message: `Could not read ${GGUI_PRIMITIVES_JSON_FILENAME}: ${errorMessage(cause)}`,
233
+ cause,
234
+ },
235
+ };
236
+ }
237
+ let decoded;
238
+ try {
239
+ decoded = JSON.parse(raw);
240
+ }
241
+ catch (cause) {
242
+ return {
243
+ ok: false,
244
+ issue: {
245
+ path: displayPath,
246
+ message: `${GGUI_PRIMITIVES_JSON_FILENAME} is not valid JSON: ${errorMessage(cause)}`,
247
+ cause,
248
+ },
249
+ };
250
+ }
251
+ try {
252
+ const manifest = parsePrimitivesManifest(decoded);
253
+ return { ok: true, manifest };
254
+ }
255
+ catch (cause) {
256
+ return {
257
+ ok: false,
258
+ issue: {
259
+ path: displayPath,
260
+ message: `${GGUI_PRIMITIVES_JSON_FILENAME} failed schema validation: ${errorMessage(cause)}`,
261
+ cause,
262
+ },
263
+ };
264
+ }
265
+ }
266
+ function recordCatalog(catalogs, issues, seenImports, candidate) {
267
+ const prior = seenImports.get(candidate.import);
268
+ if (prior !== undefined) {
269
+ issues.push({
270
+ path: candidate.manifestPath,
271
+ message: `Duplicate primitive import specifier "${candidate.import}" — already declared by ${prior}. ` +
272
+ `Two sources cannot claim the same specifier (the generator would emit duplicate import lines).`,
273
+ });
274
+ return;
275
+ }
276
+ seenImports.set(candidate.import, candidate.manifestPath);
277
+ catalogs.push(candidate);
278
+ }
279
+ function errorMessage(cause) {
280
+ return cause instanceof Error ? cause.message : String(cause);
281
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * `ggui.primitives.json` v1 — primitive package manifest.
3
+ *
4
+ * Fourth open file-format surface in the ggui manifest model, after
5
+ * `ggui.json` (root) and `ggui.ui.json` (per-UI). One file at the
6
+ * root of each primitive source:
7
+ *
8
+ * ```
9
+ * packages/design/
10
+ * ├── ggui.primitives.json ← this file
11
+ * ├── package.json
12
+ * └── src/primitives/
13
+ * ```
14
+ *
15
+ * The root `ggui.json#primitives.packages` (npm specifiers) +
16
+ * `primitives.local` (globs) declare *which* primitive sources the
17
+ * app uses; this file declares *what each source exports* + the
18
+ * `import` specifier consumers use + an optional pre-generated LLM
19
+ * docs pointer.
20
+ *
21
+ * Both `primitives.packages` (npm specifiers) and `primitives.local`
22
+ * (globs) follow the same discovery convention: the resolver walks up
23
+ * from the resolved source to the enclosing `package.json` and reads
24
+ * `ggui.primitives.json` next to it.
25
+ *
26
+ * **Ownership boundary:**
27
+ *
28
+ * - *Which* packages / local paths declare primitives → `ggui.json`.
29
+ * - *Which* primitives a source exports + import specifier + docs
30
+ * pointer → this file.
31
+ * - *What* each primitive renders / its prop interface / its
32
+ * JSDoc description → TSX source in the declaring package. NOT a
33
+ * ggui concern.
34
+ *
35
+ * **Extending rules:**
36
+ *
37
+ * 1. Additive only within `schema: '1'`. Optional fields default to
38
+ * no-op behaviour so older tooling ignores them.
39
+ * 2. Framework-neutral. Nothing Claude-SDK / Vercel-AI-SDK /
40
+ * LangGraph / framework-specific belongs here.
41
+ * 3. Root and per-primitive objects are strict — unknown keys fail
42
+ * parse. Same discipline as `ggui.json` + `ggui.ui.json`.
43
+ */
44
+ import { z } from 'zod';
45
+ /**
46
+ * Root manifest. `import` MUST be a non-empty string — either an npm
47
+ * specifier (for `primitives.packages` sources) or a relative path
48
+ * (for `primitives.local` sources). The discovery layer validates
49
+ * that `import` matches the declared `packages` entry when the
50
+ * source was resolved through an npm specifier.
51
+ */
52
+ export declare const PrimitivesManifestV1: z.ZodObject<{
53
+ /** File-format version — always `"1"` for v1. */
54
+ schema: z.ZodLiteral<"1">;
55
+ /**
56
+ * The specifier consumers import from. For npm-distributed
57
+ * primitive packages this is the same string the app lists in
58
+ * `ggui.json#primitives.packages` (e.g.
59
+ * `@ggui-ai/design/primitives`). For in-project local primitives
60
+ * it is the path consumers import from (e.g.
61
+ * `./src/ui/primitives/index.js`).
62
+ */
63
+ import: z.ZodString;
64
+ /**
65
+ * List of primitive components this source exports. At least one
66
+ * entry — a manifest that declares zero primitives is always a
67
+ * mistake.
68
+ */
69
+ primitives: z.ZodArray<z.ZodObject<{
70
+ name: z.ZodString;
71
+ }, z.core.$strict>>;
72
+ /**
73
+ * Optional path to a pre-generated LLM docs blob the generator
74
+ * can inject into the system prompt. Resolved relative to this
75
+ * manifest's directory at load time (not parse time).
76
+ *
77
+ * Absent → the server falls back to a names-only doc block and
78
+ * emits a boot warning (not fatal). The package's own build
79
+ * pipeline is responsible for emitting the file; ggui tooling
80
+ * never writes it.
81
+ */
82
+ docs: z.ZodOptional<z.ZodString>;
83
+ }, z.core.$strict>;
84
+ /** Static TypeScript type derived from the v1 schema. */
85
+ export type PrimitivesManifestV1 = z.infer<typeof PrimitivesManifestV1>;
86
+ /**
87
+ * Canonical type alias — `PrimitivesManifest` is the term used
88
+ * everywhere else (discovery, docs, mcp-server wiring).
89
+ * `PrimitivesManifestV1` is the version-pinned name.
90
+ */
91
+ export type PrimitivesManifest = PrimitivesManifestV1;
92
+ /**
93
+ * Canonical filename — always `ggui.primitives.json`, always at the
94
+ * root of the declaring package / local primitive directory.
95
+ */
96
+ export declare const GGUI_PRIMITIVES_JSON_FILENAME = "ggui.primitives.json";
97
+ /**
98
+ * Parse a raw JSON value into a validated {@link PrimitivesManifest}.
99
+ * Throws a `ZodError` with human-readable issues on invalid input.
100
+ */
101
+ export declare function parsePrimitivesManifest(raw: unknown): PrimitivesManifest;
102
+ /**
103
+ * Safe-parse variant — returns a discriminated `z.safeParse` result.
104
+ */
105
+ export declare function safeParsePrimitivesManifest(raw: unknown): ReturnType<typeof PrimitivesManifestV1.safeParse>;
106
+ //# sourceMappingURL=primitives-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives-manifest.d.ts","sourceRoot":"","sources":["../src/primitives-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiCxB;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;IAE7B,iDAAiD;;IAGjD;;;;;;;OAOG;;IAGH;;;;OAIG;;;;IASH;;;;;;;;;OASG;;kBAEH,CAAC;AAEL,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,oBAAoB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,6BAA6B,yBAAyB,CAAC;AAEpE;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAExE;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,oBAAoB,CAAC,SAAS,CAAC,CAEnD"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * `ggui.primitives.json` v1 — primitive package manifest.
3
+ *
4
+ * Fourth open file-format surface in the ggui manifest model, after
5
+ * `ggui.json` (root) and `ggui.ui.json` (per-UI). One file at the
6
+ * root of each primitive source:
7
+ *
8
+ * ```
9
+ * packages/design/
10
+ * ├── ggui.primitives.json ← this file
11
+ * ├── package.json
12
+ * └── src/primitives/
13
+ * ```
14
+ *
15
+ * The root `ggui.json#primitives.packages` (npm specifiers) +
16
+ * `primitives.local` (globs) declare *which* primitive sources the
17
+ * app uses; this file declares *what each source exports* + the
18
+ * `import` specifier consumers use + an optional pre-generated LLM
19
+ * docs pointer.
20
+ *
21
+ * Both `primitives.packages` (npm specifiers) and `primitives.local`
22
+ * (globs) follow the same discovery convention: the resolver walks up
23
+ * from the resolved source to the enclosing `package.json` and reads
24
+ * `ggui.primitives.json` next to it.
25
+ *
26
+ * **Ownership boundary:**
27
+ *
28
+ * - *Which* packages / local paths declare primitives → `ggui.json`.
29
+ * - *Which* primitives a source exports + import specifier + docs
30
+ * pointer → this file.
31
+ * - *What* each primitive renders / its prop interface / its
32
+ * JSDoc description → TSX source in the declaring package. NOT a
33
+ * ggui concern.
34
+ *
35
+ * **Extending rules:**
36
+ *
37
+ * 1. Additive only within `schema: '1'`. Optional fields default to
38
+ * no-op behaviour so older tooling ignores them.
39
+ * 2. Framework-neutral. Nothing Claude-SDK / Vercel-AI-SDK /
40
+ * LangGraph / framework-specific belongs here.
41
+ * 3. Root and per-primitive objects are strict — unknown keys fail
42
+ * parse. Same discipline as `ggui.json` + `ggui.ui.json`.
43
+ */
44
+ import { z } from 'zod';
45
+ /**
46
+ * Primitive component name — must be a valid JavaScript identifier.
47
+ * Stricter than a freeform string because the generator emits literal
48
+ * `import { <name> } from '<import>'` lines, and anything that isn't
49
+ * a valid identifier would break the TSX output.
50
+ *
51
+ * Also rejects reserved words implicitly via the "leading letter"
52
+ * rule — JS identifiers can't start with a digit. A downstream
53
+ * allowlist of reserved words is avoided here on purpose: the list
54
+ * is long, version-dependent, and would add maintenance burden
55
+ * without meaningfully changing what authors actually write.
56
+ */
57
+ const PrimitiveNameSchema = z
58
+ .string()
59
+ .min(1)
60
+ .max(64)
61
+ .regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/, {
62
+ message: 'primitive name must be a valid JavaScript identifier (letters, digits, _, $; not starting with a digit).',
63
+ });
64
+ /**
65
+ * Per-primitive entry. v1 only requires `name`; the TSX source +
66
+ * JSDoc is the authoritative prop-surface. Additive fields
67
+ * (description override, deprecation marker, variant hint) land
68
+ * later under `schema: "1"`.
69
+ */
70
+ const PrimitiveEntrySchema = z.strictObject({
71
+ name: PrimitiveNameSchema,
72
+ });
73
+ /**
74
+ * Root manifest. `import` MUST be a non-empty string — either an npm
75
+ * specifier (for `primitives.packages` sources) or a relative path
76
+ * (for `primitives.local` sources). The discovery layer validates
77
+ * that `import` matches the declared `packages` entry when the
78
+ * source was resolved through an npm specifier.
79
+ */
80
+ export const PrimitivesManifestV1 = z
81
+ .strictObject({
82
+ /** File-format version — always `"1"` for v1. */
83
+ schema: z.literal('1'),
84
+ /**
85
+ * The specifier consumers import from. For npm-distributed
86
+ * primitive packages this is the same string the app lists in
87
+ * `ggui.json#primitives.packages` (e.g.
88
+ * `@ggui-ai/design/primitives`). For in-project local primitives
89
+ * it is the path consumers import from (e.g.
90
+ * `./src/ui/primitives/index.js`).
91
+ */
92
+ import: z.string().min(1, 'import must not be empty'),
93
+ /**
94
+ * List of primitive components this source exports. At least one
95
+ * entry — a manifest that declares zero primitives is always a
96
+ * mistake.
97
+ */
98
+ primitives: z
99
+ .array(PrimitiveEntrySchema)
100
+ .min(1, 'primitives must declare at least one entry')
101
+ .refine((arr) => new Set(arr.map((p) => p.name)).size === arr.length, { message: 'primitive names must be unique within a manifest' }),
102
+ /**
103
+ * Optional path to a pre-generated LLM docs blob the generator
104
+ * can inject into the system prompt. Resolved relative to this
105
+ * manifest's directory at load time (not parse time).
106
+ *
107
+ * Absent → the server falls back to a names-only doc block and
108
+ * emits a boot warning (not fatal). The package's own build
109
+ * pipeline is responsible for emitting the file; ggui tooling
110
+ * never writes it.
111
+ */
112
+ docs: z.string().min(1).optional(),
113
+ });
114
+ /**
115
+ * Canonical filename — always `ggui.primitives.json`, always at the
116
+ * root of the declaring package / local primitive directory.
117
+ */
118
+ export const GGUI_PRIMITIVES_JSON_FILENAME = 'ggui.primitives.json';
119
+ /**
120
+ * Parse a raw JSON value into a validated {@link PrimitivesManifest}.
121
+ * Throws a `ZodError` with human-readable issues on invalid input.
122
+ */
123
+ export function parsePrimitivesManifest(raw) {
124
+ return PrimitivesManifestV1.parse(raw);
125
+ }
126
+ /**
127
+ * Safe-parse variant — returns a discriminated `z.safeParse` result.
128
+ */
129
+ export function safeParsePrimitivesManifest(raw) {
130
+ return PrimitivesManifestV1.safeParse(raw);
131
+ }