@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,226 @@
1
+ /**
2
+ * MCP mount discovery — turns a loaded `ggui.json#mcpMounts` list into
3
+ * a resolved, imported, factory-invoked set of mount payloads the
4
+ * OSS `ggui serve` path threads into
5
+ * `createGguiServer({ mcpMounts: ... })`.
6
+ *
7
+ * The flow per spec:
8
+ *
9
+ * string (spec)
10
+ * → resolve to absolute file path (relative → project root;
11
+ * bare → Node module resolver)
12
+ * → dynamic-import
13
+ * → pick `createGguiMcpMount` or default export
14
+ * → invoke (zero-arg; awaits Promise)
15
+ * → structural validate `{ name: string; handlers: array }`
16
+ *
17
+ * This module is **type-thin**: it does NOT import `McpServerMount`
18
+ * from `@ggui-ai/mcp-server`, keeping `@ggui-ai/project-config`'s dep
19
+ * graph honest (no reverse dep from config → server). The CLI
20
+ * (`@ggui-ai/cli`), which already pulls `@ggui-ai/mcp-server`,
21
+ * narrows the returned `handlers: unknown[]` to the concrete
22
+ * `SharedHandler[]` at the adapter boundary. Down-layer collision
23
+ * checks + per-handler validation happen inside
24
+ * `composeHandlersWithMounts` when `createGguiServer` runs.
25
+ *
26
+ * ## Error model
27
+ *
28
+ * Non-throwing per source — one malformed mount does not black-hole
29
+ * the rest of the list. Every failure mode surfaces as an entry in
30
+ * `issues`, with the declared spec on `.path` so renderers can group
31
+ * issues by source. Callers that need fail-fast (`ggui serve` boot)
32
+ * inspect `result.issues` themselves and escalate to a fatal exit.
33
+ * Mirrors the blueprint / primitive discovery split.
34
+ *
35
+ * ## Factory contract lock
36
+ *
37
+ * The resolved module must export either:
38
+ *
39
+ * - a named `createGguiMcpMount` function, OR
40
+ * - a default export that is a function.
41
+ *
42
+ * Named export wins if both are present. The function receives zero
43
+ * arguments today; passing options is a future additive (widening the
44
+ * `ggui.json#mcpMounts` entry from `string` to `string | { module,
45
+ * options? }` and threading `options` through here).
46
+ *
47
+ * Mount factories are responsible for their own backing state —
48
+ * instantiating stores, seeding fixtures, wiring adapters — so the
49
+ * operator's config stays data-only. The factory may be `async`.
50
+ */
51
+ import { existsSync } from 'node:fs';
52
+ import { createRequire } from 'node:module';
53
+ import { isAbsolute, join, resolve } from 'node:path';
54
+ import { pathToFileURL } from 'node:url';
55
+ /**
56
+ * The export name callers should use on their mount module. A default
57
+ * export is also accepted when this named export is absent; the named
58
+ * form is preferred because it gives readers a visible symbol at the
59
+ * declaration site (`export function createGguiMcpMount() { ... }`).
60
+ */
61
+ export const GGUI_MCP_MOUNT_FACTORY_EXPORT = 'createGguiMcpMount';
62
+ /**
63
+ * Walk `ggui.json#mcpMounts` and produce resolved + invoked mount
64
+ * payloads. Non-throwing per source; each failure lands on `issues`.
65
+ *
66
+ * Ordering: `mounts` preserves declaration order (so collision errors
67
+ * inside `composeHandlersWithMounts` deterministically blame the
68
+ * later-declared duplicate). `issues` likewise preserves declaration
69
+ * order so CLI renderers can print "issue #1 first" if they want.
70
+ */
71
+ export async function discoverMcpMounts(options) {
72
+ const { projectRoot, manifest } = options;
73
+ if (!isAbsolute(projectRoot)) {
74
+ throw new Error(`discoverMcpMounts: projectRoot must be absolute, got ${projectRoot}`);
75
+ }
76
+ const specs = manifest.mcpMounts;
77
+ const mounts = [];
78
+ const issues = [];
79
+ if (specs.length === 0)
80
+ return { mounts, issues };
81
+ const resolveModule = options.resolveModule ?? makeDefaultResolver(projectRoot);
82
+ const importModule = options.importModule ?? ((url) => import(url));
83
+ for (const spec of specs) {
84
+ let modulePath;
85
+ try {
86
+ modulePath = resolveModule(spec, projectRoot);
87
+ }
88
+ catch (cause) {
89
+ issues.push({
90
+ path: spec,
91
+ message: `Could not resolve mcpMounts entry "${spec}" — is the file present or the package installed? ` +
92
+ `(${errorMessage(cause)})`,
93
+ cause,
94
+ });
95
+ continue;
96
+ }
97
+ let mod;
98
+ try {
99
+ mod = await importModule(pathToFileURL(modulePath).href);
100
+ }
101
+ catch (cause) {
102
+ issues.push({
103
+ path: spec,
104
+ message: `Failed to import mcpMounts entry "${spec}" (${modulePath}): ${errorMessage(cause)}`,
105
+ cause,
106
+ });
107
+ continue;
108
+ }
109
+ const factory = pickFactory(mod);
110
+ if (factory === null) {
111
+ issues.push({
112
+ path: spec,
113
+ message: `mcpMounts entry "${spec}" must export a \`${GGUI_MCP_MOUNT_FACTORY_EXPORT}\` function ` +
114
+ `(or a default export that is a function) returning { name, handlers }. Got: ${describeModule(mod)}.`,
115
+ });
116
+ continue;
117
+ }
118
+ let payload;
119
+ try {
120
+ payload = await factory();
121
+ }
122
+ catch (cause) {
123
+ issues.push({
124
+ path: spec,
125
+ message: `mcpMounts entry "${spec}" factory threw during invocation: ${errorMessage(cause)}`,
126
+ cause,
127
+ });
128
+ continue;
129
+ }
130
+ const validated = validateMountShape(payload);
131
+ if (!validated.ok) {
132
+ issues.push({
133
+ path: spec,
134
+ message: `mcpMounts entry "${spec}" factory returned an unexpected shape: ${validated.message}`,
135
+ });
136
+ continue;
137
+ }
138
+ mounts.push({
139
+ spec,
140
+ modulePath,
141
+ mount: validated.mount,
142
+ });
143
+ }
144
+ return { mounts, issues };
145
+ }
146
+ // ─────────────────────────────────────────────────────────────────────
147
+ // Internals
148
+ // ─────────────────────────────────────────────────────────────────────
149
+ /**
150
+ * Default resolver: relative paths resolve filesystem-wise against
151
+ * the project root (with an `existsSync` gate so a missing file
152
+ * produces a sharp error message, not a noisy `createRequire` trace);
153
+ * bare specifiers resolve through `createRequire` anchored at the
154
+ * project root.
155
+ */
156
+ function makeDefaultResolver(projectRoot) {
157
+ // Anchor at `<projectRoot>/package.json` — same convention the
158
+ // primitive resolver uses. `createRequire` doesn't require the
159
+ // anchor file to exist; Node uses the path only to establish the
160
+ // ancestor `node_modules/` resolution chain.
161
+ const originUrl = pathToFileURL(join(projectRoot, 'package.json')).href;
162
+ const req = createRequire(originUrl);
163
+ return (spec) => {
164
+ if (spec.startsWith('./') || spec.startsWith('../') || isAbsolute(spec)) {
165
+ const resolved = resolve(projectRoot, spec);
166
+ if (!existsSync(resolved)) {
167
+ throw new Error(`relative mcpMounts path "${spec}" resolved to ${resolved}, which does not exist`);
168
+ }
169
+ return resolved;
170
+ }
171
+ return req.resolve(spec);
172
+ };
173
+ }
174
+ function pickFactory(mod) {
175
+ if (mod === null || typeof mod !== 'object')
176
+ return null;
177
+ const record = mod;
178
+ const named = record[GGUI_MCP_MOUNT_FACTORY_EXPORT];
179
+ if (typeof named === 'function') {
180
+ return named;
181
+ }
182
+ const dflt = record['default'];
183
+ if (typeof dflt === 'function') {
184
+ return dflt;
185
+ }
186
+ return null;
187
+ }
188
+ function validateMountShape(payload) {
189
+ if (payload === null || typeof payload !== 'object') {
190
+ return {
191
+ ok: false,
192
+ message: `expected an object shaped { name: string; handlers: [] }, got ${typeof payload}`,
193
+ };
194
+ }
195
+ const obj = payload;
196
+ const name = obj['name'];
197
+ if (typeof name !== 'string' || name.length === 0) {
198
+ return {
199
+ ok: false,
200
+ message: `\`name\` must be a non-empty string (used in collision-error messages + composition telemetry)`,
201
+ };
202
+ }
203
+ const handlers = obj['handlers'];
204
+ if (!Array.isArray(handlers)) {
205
+ return {
206
+ ok: false,
207
+ message: `\`handlers\` must be an array (received: ${typeof handlers})`,
208
+ };
209
+ }
210
+ return {
211
+ ok: true,
212
+ mount: {
213
+ name,
214
+ handlers: handlers,
215
+ },
216
+ };
217
+ }
218
+ function describeModule(mod) {
219
+ if (mod === null || typeof mod !== 'object')
220
+ return typeof mod;
221
+ const keys = Object.keys(mod);
222
+ return keys.length === 0 ? '(no exports)' : `exports: ${keys.join(', ')}`;
223
+ }
224
+ function errorMessage(cause) {
225
+ return cause instanceof Error ? cause.message : String(cause);
226
+ }
package/dist/node.d.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { GguiJsonV1 } from './schema.js';
2
+ /** How many parent directories `findGguiJson` will walk by default. */
3
+ export declare const DEFAULT_FIND_MAX_DEPTH = 8;
4
+ /**
5
+ * Walk up from `startDir` (default: `process.cwd()`) looking for a
6
+ * `ggui.json`. Returns the absolute path to the first match, or
7
+ * `null` if no file is found within `maxDepth` levels.
8
+ *
9
+ * Stops when the filesystem root is reached, regardless of `maxDepth`.
10
+ * Never throws — a missing file is a valid result ("not in a ggui
11
+ * project"), not an error.
12
+ */
13
+ export declare function findGguiJson(startDir?: string, maxDepth?: number): string | null;
14
+ /**
15
+ * Error thrown when a `ggui.json` fails to load — missing file,
16
+ * malformed JSON, or schema validation failure. Wraps the underlying
17
+ * cause (`SyntaxError` / `ZodError`) on `.cause` so callers can
18
+ * inspect issue lists when they need to.
19
+ */
20
+ export declare class GguiJsonLoadError extends Error {
21
+ readonly path: string;
22
+ constructor(message: string, path: string, options?: {
23
+ cause?: unknown;
24
+ });
25
+ }
26
+ /**
27
+ * Read `ggui.json` at `path`, parse JSON, validate against the v1
28
+ * schema. Returns the fully-defaulted document.
29
+ *
30
+ * Throws {@link GguiJsonLoadError} if:
31
+ * - the file does not exist,
32
+ * - the file is not valid JSON,
33
+ * - the document fails schema validation (cause set to `ZodError`).
34
+ */
35
+ export declare function loadGguiJson(path: string): GguiJsonV1;
36
+ /**
37
+ * Result of {@link safeLoadGguiJson} — mirrors `z.safeParse` so
38
+ * consumers can branch without try/catch.
39
+ */
40
+ export type SafeLoadResult = {
41
+ success: true;
42
+ data: GguiJsonV1;
43
+ } | {
44
+ success: false;
45
+ error: GguiJsonLoadError;
46
+ };
47
+ /**
48
+ * Non-throwing variant of {@link loadGguiJson}. Returns a
49
+ * discriminated result. Use this in CLI surfaces that render issue
50
+ * lists directly.
51
+ */
52
+ export declare function safeLoadGguiJson(path: string): SafeLoadResult;
53
+ /**
54
+ * Serialize and write a `ggui.json` document to `path`. The input is
55
+ * re-validated against the v1 schema before writing so a caller
56
+ * cannot accidentally persist a document that wouldn't round-trip
57
+ * through {@link loadGguiJson}.
58
+ *
59
+ * Output format: 2-space indent, trailing newline.
60
+ */
61
+ export declare function saveGguiJson(path: string, doc: GguiJsonV1): void;
62
+ export { GGUI_JSON_FILENAME, GguiJsonV1, parseGguiJson, safeParseGguiJson, } from './schema.js';
63
+ export { discoverLocalUis, discoverFromGguiJsonPath, type DiscoveredUi, type DiscoveryIssue, type DiscoveryResult, type DiscoverOptions, } from './discovery.js';
64
+ export { discoverPrimitives, discoverPrimitivesFromGguiJsonPath, type DiscoveredPrimitiveCatalog, type PrimitiveDiscoveryIssue, type PrimitiveDiscoveryResult, type DiscoverPrimitivesOptions, } from './primitive-discovery.js';
65
+ export { discoverMcpMounts, GGUI_MCP_MOUNT_FACTORY_EXPORT, type DiscoveredMcpMount, type McpMountDiscoveryIssue, type McpMountDiscoveryResult, type DiscoverMcpMountsOptions, } from './mcp-mount-discovery.js';
66
+ export { GGUI_PRIMITIVES_JSON_FILENAME, PrimitivesManifestV1, parsePrimitivesManifest, safeParsePrimitivesManifest, type PrimitivesManifest, } from './primitives-manifest.js';
67
+ export { ThemeDocumentV1, parseThemeDocument, safeParseThemeDocument, type ThemeDocument, } from './theme.js';
68
+ export { loadTheme, loadThemeFromGguiJsonPath, type LoadedTheme, type LoadThemeOptions, type LoadThemeResult, type ThemeLoadIssue, } from './theme-loader.js';
69
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAqBA,OAAO,EAEL,UAAU,EAEX,MAAM,aAAa,CAAC;AAErB,uEAAuE;AACvE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,GAAE,MAAsB,EAChC,QAAQ,GAAE,MAA+B,GACxC,MAAM,GAAG,IAAI,CAUf;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAKzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAoCrD;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GACnC;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAEjD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAS7D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAGhE;AAMD,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAMrB,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AAOxB,OAAO,EACL,kBAAkB,EAClB,kCAAkC,EAClC,KAAK,0BAA0B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,0BAA0B,CAAC;AAOlC,OAAO,EACL,iBAAiB,EACjB,6BAA6B,EAC7B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC9B,MAAM,0BAA0B,CAAC;AAIlC,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,kBAAkB,GACxB,MAAM,0BAA0B,CAAC;AAMlC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AAMpB,OAAO,EACL,SAAS,EACT,yBAAyB,EACzB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,mBAAmB,CAAC"}
package/dist/node.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Node-only filesystem helpers for `ggui.json`.
3
+ *
4
+ * Lives on the `@ggui-ai/project-config/node` subpath (not the root
5
+ * barrel) because it imports `node:fs` / `node:path`. Pulling those
6
+ * builtins into the root barrel would break browser bundlers that
7
+ * resolve `@ggui-ai/project-config` directly (webpack refuses the
8
+ * `node:` scheme). The schema + parser in `./schema.ts` stay
9
+ * browser-safe and are re-exported both from here and from the root
10
+ * barrel.
11
+ *
12
+ * Intended callers:
13
+ *
14
+ * - Open `ggui` CLI (local dev / run / pair / serve / bench). Walks
15
+ * upward from `process.cwd()` to find the project-root file.
16
+ * - Any hosting integration that reads the open manifest to
17
+ * reconstitute an agent. Hosting layers treat `ggui.json` as
18
+ * input-only; per-tenant overlay config lives outside this package.
19
+ */
20
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
21
+ import { dirname, join, resolve } from 'node:path';
22
+ import { GGUI_JSON_FILENAME, parseGguiJson, } from './schema.js';
23
+ /** How many parent directories `findGguiJson` will walk by default. */
24
+ export const DEFAULT_FIND_MAX_DEPTH = 8;
25
+ /**
26
+ * Walk up from `startDir` (default: `process.cwd()`) looking for a
27
+ * `ggui.json`. Returns the absolute path to the first match, or
28
+ * `null` if no file is found within `maxDepth` levels.
29
+ *
30
+ * Stops when the filesystem root is reached, regardless of `maxDepth`.
31
+ * Never throws — a missing file is a valid result ("not in a ggui
32
+ * project"), not an error.
33
+ */
34
+ export function findGguiJson(startDir = process.cwd(), maxDepth = DEFAULT_FIND_MAX_DEPTH) {
35
+ let dir = resolve(startDir);
36
+ for (let i = 0; i <= maxDepth; i++) {
37
+ const candidate = join(dir, GGUI_JSON_FILENAME);
38
+ if (existsSync(candidate))
39
+ return candidate;
40
+ const parent = dirname(dir);
41
+ if (parent === dir)
42
+ return null;
43
+ dir = parent;
44
+ }
45
+ return null;
46
+ }
47
+ /**
48
+ * Error thrown when a `ggui.json` fails to load — missing file,
49
+ * malformed JSON, or schema validation failure. Wraps the underlying
50
+ * cause (`SyntaxError` / `ZodError`) on `.cause` so callers can
51
+ * inspect issue lists when they need to.
52
+ */
53
+ export class GguiJsonLoadError extends Error {
54
+ path;
55
+ constructor(message, path, options) {
56
+ super(message, options);
57
+ this.name = 'GguiJsonLoadError';
58
+ this.path = path;
59
+ }
60
+ }
61
+ /**
62
+ * Read `ggui.json` at `path`, parse JSON, validate against the v1
63
+ * schema. Returns the fully-defaulted document.
64
+ *
65
+ * Throws {@link GguiJsonLoadError} if:
66
+ * - the file does not exist,
67
+ * - the file is not valid JSON,
68
+ * - the document fails schema validation (cause set to `ZodError`).
69
+ */
70
+ export function loadGguiJson(path) {
71
+ if (!existsSync(path)) {
72
+ throw new GguiJsonLoadError(`ggui.json not found at ${path}`, path);
73
+ }
74
+ let raw;
75
+ try {
76
+ raw = readFileSync(path, 'utf-8');
77
+ }
78
+ catch (cause) {
79
+ throw new GguiJsonLoadError(`Failed to read ggui.json at ${path}`, path, { cause });
80
+ }
81
+ let decoded;
82
+ try {
83
+ decoded = JSON.parse(raw);
84
+ }
85
+ catch (cause) {
86
+ throw new GguiJsonLoadError(`ggui.json at ${path} is not valid JSON`, path, { cause });
87
+ }
88
+ try {
89
+ return parseGguiJson(decoded);
90
+ }
91
+ catch (cause) {
92
+ throw new GguiJsonLoadError(`ggui.json at ${path} failed schema validation`, path, { cause });
93
+ }
94
+ }
95
+ /**
96
+ * Non-throwing variant of {@link loadGguiJson}. Returns a
97
+ * discriminated result. Use this in CLI surfaces that render issue
98
+ * lists directly.
99
+ */
100
+ export function safeLoadGguiJson(path) {
101
+ try {
102
+ return { success: true, data: loadGguiJson(path) };
103
+ }
104
+ catch (error) {
105
+ if (error instanceof GguiJsonLoadError) {
106
+ return { success: false, error };
107
+ }
108
+ throw error;
109
+ }
110
+ }
111
+ /**
112
+ * Serialize and write a `ggui.json` document to `path`. The input is
113
+ * re-validated against the v1 schema before writing so a caller
114
+ * cannot accidentally persist a document that wouldn't round-trip
115
+ * through {@link loadGguiJson}.
116
+ *
117
+ * Output format: 2-space indent, trailing newline.
118
+ */
119
+ export function saveGguiJson(path, doc) {
120
+ const validated = parseGguiJson(doc);
121
+ writeFileSync(path, JSON.stringify(validated, null, 2) + '\n', 'utf-8');
122
+ }
123
+ // Re-export schema + parser from the same subpath so Node callers
124
+ // can do `import { parseGguiJson, loadGguiJson } from
125
+ // '@ggui-ai/project-config/node'` without two imports. Browser callers
126
+ // use the root barrel, which exports the browser-safe subset.
127
+ export { GGUI_JSON_FILENAME, GguiJsonV1, parseGguiJson, safeParseGguiJson, } from './schema.js';
128
+ // Filesystem-backed blueprint manifest discovery. Lives here (not the
129
+ // browser-safe root barrel) because it imports `node:fs/promises` +
130
+ // `tinyglobby`. Consumed by `@ggui-ai/dev-stack` (for the `ggui dev`
131
+ // hub) and `@ggui-ai/mcp-server` (for `ggui serve` boot wiring).
132
+ export { discoverLocalUis, discoverFromGguiJsonPath, } from './discovery.js';
133
+ // Filesystem + Node-module-resolution backed primitive manifest
134
+ // discovery. Symmetric with blueprint discovery above but resolves
135
+ // npm specifiers through `createRequire` rather than glob-only.
136
+ // Consumed by `@ggui-ai/mcp-server` at `ggui serve` boot to index
137
+ // every primitive source declared in `ggui.json#primitives`.
138
+ export { discoverPrimitives, discoverPrimitivesFromGguiJsonPath, } from './primitive-discovery.js';
139
+ // Node-only resolution of `ggui.json#mcpMounts`. Dynamic-imports each
140
+ // declared module, invokes its `createGguiMcpMount` factory, and
141
+ // returns structurally-validated mount payloads. Consumed by
142
+ // `@ggui-ai/cli` at `ggui serve` boot to thread operator-declared
143
+ // tool bundles through `createGguiServer({ mcpMounts })`.
144
+ export { discoverMcpMounts, GGUI_MCP_MOUNT_FACTORY_EXPORT, } from './mcp-mount-discovery.js';
145
+ // Browser-safe primitive manifest schema + parsers re-exported here
146
+ // too so Node callers get the full surface from one import path.
147
+ export { GGUI_PRIMITIVES_JSON_FILENAME, PrimitivesManifestV1, parsePrimitivesManifest, safeParsePrimitivesManifest, } from './primitives-manifest.js';
148
+ // Browser-safe theme document schema + parsers — re-exported on the
149
+ // Node subpath so `ggui serve` / `ggui dev` get the full theme
150
+ // surface from one import path. See `./theme.js` for the v1
151
+ // plain-DTCG contract.
152
+ export { ThemeDocumentV1, parseThemeDocument, safeParseThemeDocument, } from './theme.js';
153
+ // Filesystem-backed theme loader — consumes `ggui.json#theme`,
154
+ // resolves it against the project root, parses + validates, and
155
+ // returns a `LoadedTheme` ready for the OSS `ggui serve` server.
156
+ // Non-throwing per-file; the CLI escalates issues to fatal at boot.
157
+ export { loadTheme, loadThemeFromGguiJsonPath, } from './theme-loader.js';
@@ -0,0 +1,95 @@
1
+ import type { GguiJsonV1 } from './schema.js';
2
+ import { type PrimitivesManifest } from './primitives-manifest.js';
3
+ /**
4
+ * One discovered primitive catalog — the import specifier + absolute
5
+ * manifest path + the parsed manifest. Callers address by `import`;
6
+ * paths drive doc-file lookups and informational output.
7
+ */
8
+ export interface DiscoveredPrimitiveCatalog {
9
+ /**
10
+ * Where the source came from. `'package'` = resolved through an
11
+ * npm specifier declared in `ggui.json#primitives.packages`;
12
+ * `'local'` = matched by a glob in `ggui.json#primitives.local`.
13
+ * Renderers may use this to annotate the catalog differently.
14
+ */
15
+ source: 'package' | 'local';
16
+ /**
17
+ * The specifier consumers import from (equal to `manifest.import`).
18
+ * Keyed separately from the manifest so callers don't have to
19
+ * reach into the nested shape for the common case.
20
+ */
21
+ import: string;
22
+ /** Absolute path to the discovered `ggui.primitives.json`. */
23
+ manifestPath: string;
24
+ /** Parsed manifest. */
25
+ manifest: PrimitivesManifest;
26
+ }
27
+ /**
28
+ * An error surfaced during discovery. Shape matches
29
+ * {@link DiscoveryIssue} from blueprint discovery so CLI renderers
30
+ * can treat both issue streams identically.
31
+ */
32
+ export interface PrimitiveDiscoveryIssue {
33
+ /**
34
+ * Path or specifier of the offending source. For `packages`
35
+ * entries this is the declared npm specifier (not a filesystem
36
+ * path) since resolution may have failed before we could locate
37
+ * a file. For `local` entries this is a relative path under the
38
+ * project root.
39
+ */
40
+ path: string;
41
+ message: string;
42
+ /** Underlying cause (typically `ZodError`, `SyntaxError`, or the resolver error). */
43
+ cause?: unknown;
44
+ }
45
+ /** Result of a full primitive-discovery pass. */
46
+ export interface PrimitiveDiscoveryResult {
47
+ /** Successfully parsed catalogs. Ordering: packages first (in declaration order),
48
+ * then local (filesystem-walk order from tinyglobby). */
49
+ catalogs: DiscoveredPrimitiveCatalog[];
50
+ /** Issues that should be rendered to the user; discovery still returns. */
51
+ issues: PrimitiveDiscoveryIssue[];
52
+ }
53
+ /**
54
+ * Options for {@link discoverPrimitives}. `resolveModule` is exposed
55
+ * so tests can inject a fake resolver; real callers omit it and get
56
+ * the Node `createRequire` default anchored at `projectRoot`.
57
+ */
58
+ export interface DiscoverPrimitivesOptions {
59
+ /**
60
+ * Absolute project root — the directory containing `ggui.json`.
61
+ * Globs in `primitives.local` resolve relative to this directory;
62
+ * npm specifiers in `primitives.packages` resolve through a
63
+ * `require` anchored here.
64
+ */
65
+ projectRoot: string;
66
+ /** The parsed `ggui.json`. Only `primitives.{packages,local}` is consumed. */
67
+ manifest: GguiJsonV1;
68
+ /**
69
+ * Hook for tests — given the declared specifier and the project
70
+ * root, return the absolute path to a representative file inside
71
+ * the package (any file that lets us walk UP to the enclosing
72
+ * `package.json`). Throw to signal "not resolvable". Production
73
+ * callers omit this; the default resolver uses `createRequire`
74
+ * against `<projectRoot>/package.json`.
75
+ */
76
+ resolveModule?: (specifier: string, projectRoot: string) => string;
77
+ }
78
+ /**
79
+ * Walk both `primitives.packages` and `primitives.local`, build the
80
+ * catalog, accumulate issues.
81
+ *
82
+ * Non-throwing per source. Duplicate-`import`-specifier across
83
+ * sources is an issue (first-seen wins, later ones accumulate as
84
+ * issues). A glob pattern that matches nothing is allowed — the
85
+ * manifest may exist ahead of primitives being authored; operators
86
+ * can still run `ggui dev` without the catalog being complete.
87
+ */
88
+ export declare function discoverPrimitives(options: DiscoverPrimitivesOptions): Promise<PrimitiveDiscoveryResult>;
89
+ /**
90
+ * Convenience wrapper for the common "I have a `ggui.json` path,
91
+ * give me the primitives" flow. Resolves the project root from the
92
+ * manifest path.
93
+ */
94
+ export declare function discoverPrimitivesFromGguiJsonPath(manifestPath: string, manifest: GguiJsonV1, extra?: Omit<DiscoverPrimitivesOptions, 'projectRoot' | 'manifest'>): Promise<PrimitiveDiscoveryResult>;
95
+ //# sourceMappingURL=primitive-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitive-discovery.d.ts","sourceRoot":"","sources":["../src/primitive-discovery.ts"],"names":[],"mappings":"AAyDA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB;IACvB,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,iDAAiD;AACjD,MAAM,WAAW,wBAAwB;IACvC;8DAC0D;IAC1D,QAAQ,EAAE,0BAA0B,EAAE,CAAC;IACvC,2EAA2E;IAC3E,MAAM,EAAE,uBAAuB,EAAE,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,EAAE,UAAU,CAAC;IACrB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;CACpE;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC,CA0HnC;AAED;;;;GAIG;AACH,wBAAsB,kCAAkC,CACtD,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,UAAU,EACpB,KAAK,GAAE,IAAI,CAAC,yBAAyB,EAAE,aAAa,GAAG,UAAU,CAAM,GACtE,OAAO,CAAC,wBAAwB,CAAC,CAGnC"}