@happyvertical/smrt-core 0.51.7 → 0.51.9
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/README.md +26 -0
- package/agents/build-knowledge.md +13 -0
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +141 -49
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/manifest/discover-smrt-packages.d.ts +3 -2
- package/dist/manifest/discover-smrt-packages.d.ts.map +1 -1
- package/dist/manifest/discover-smrt-packages.js +34 -15
- package/dist/manifest/discover-smrt-packages.js.map +1 -1
- package/dist/manifest/package-manifest-exports.d.ts +34 -0
- package/dist/manifest/package-manifest-exports.d.ts.map +1 -0
- package/dist/manifest/package-manifest-exports.js +144 -0
- package/dist/manifest/package-manifest-exports.js.map +1 -0
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +93 -3
- package/dist/vite-plugin/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -191,6 +191,32 @@ generates routes, types, registration, and virtual modules, but it disables
|
|
|
191
191
|
source/package scans, watch rescans, and manifest writes. Omit
|
|
192
192
|
`generationSnapshot` for normal local development.
|
|
193
193
|
|
|
194
|
+
#### Where consumer manifests are resolved
|
|
195
|
+
|
|
196
|
+
`smrtConsumer()` resolves each consumed package's manifest through that
|
|
197
|
+
package's own `package.json#exports` map first — the `./manifest.json` entry
|
|
198
|
+
(or its `./manifest` alias), which every s-m-r-t model package is required
|
|
199
|
+
to publish — and only then falls back to the conventional
|
|
200
|
+
`dist/manifest/static-manifest.js`, `dist/manifest.json`, and `manifest.json`
|
|
201
|
+
paths. A package whose build emits elsewhere (for example a triple-purpose
|
|
202
|
+
package that maps `"./manifest.json": "./dist/lib/manifest.json"`) therefore
|
|
203
|
+
loads normally.
|
|
204
|
+
|
|
205
|
+
Every declared target is a candidate, not a single choice: an export array is
|
|
206
|
+
a fallback list and each recognized condition contributes its own target, so a
|
|
207
|
+
missing first entry does not hide a valid later one. Candidates are probed in
|
|
208
|
+
order and the first one that loads wins, so a candidate that exists but cannot
|
|
209
|
+
be read, parsed, or imported is reported and skipped rather than ending the
|
|
210
|
+
search — the conventional paths stay reachable behind a stale or malformed
|
|
211
|
+
export target. Targets that resolve outside the package directory are dropped,
|
|
212
|
+
both lexically and after following symlinks; the package root is itself
|
|
213
|
+
resolved through symlinks first, so pnpm and workspace installs are unaffected.
|
|
214
|
+
|
|
215
|
+
A package that yields no usable manifest is never skipped silently. An entry in
|
|
216
|
+
an explicit `packages` list is an assertion, so it fails the build by name;
|
|
217
|
+
a package found by dependency-name discovery only warns by name and contributes
|
|
218
|
+
no objects.
|
|
219
|
+
|
|
194
220
|
#### Consumer web collection definitions
|
|
195
221
|
|
|
196
222
|
`smrtConsumer()` also resolves `@smrt/web` for browser collection consumers:
|
|
@@ -36,6 +36,19 @@ owned by each consumer.
|
|
|
36
36
|
Keep `manifest.json` runtime-focused. `smrt-knowledge.json` is the deterministic
|
|
37
37
|
agent contract for downstream review and architecture tools.
|
|
38
38
|
|
|
39
|
+
The CLI's schema-command gate reads that same merged file: a pure-consumer
|
|
40
|
+
project (0 local objects) qualifies on the consumed packages its manifest
|
|
41
|
+
records, so a clobbered `objects` map is what made `smrt db:migrate` report
|
|
42
|
+
`missing_local_manifest` (#2925).
|
|
43
|
+
|
|
44
|
+
`.smrt/manifest.json` has two writers and neither owns all of it: `smrtPlugin()`
|
|
45
|
+
contributes the project's scanned objects and `smrtConsumer()` the consumed
|
|
46
|
+
packages' entries. Both writes are merge-preserving, keyed on each entry's
|
|
47
|
+
recorded `packageName` — a plain overwrite in either direction silently drops
|
|
48
|
+
the other's objects, and the last pass of a multi-environment build is not
|
|
49
|
+
reliably the aggregating one (#1760, #2925). `dist/manifest.json` stays
|
|
50
|
+
local-only: a published package must not carry its dependencies' objects.
|
|
51
|
+
|
|
39
52
|
The schema-version-1 object projection is additive and high-signal: it retains
|
|
40
53
|
normalized tenant mode/field, explicit `cti`/`sti` strategy, conflict columns,
|
|
41
54
|
method signatures, and field defaults/constraints/readonly/transient flags.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAYnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,uCAAuC,CAAC;AAoB/C,OAAO,EACL,kCAAkC,EAClC,KAAK,sCAAsC,EAC3C,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAqDnC;;;;GAIG;AACH,MAAM,WAAW,4BACf,SAAQ,OAAO,CACb,IAAI,CACF,gBAAgB,EACd,WAAW,GACX,YAAY,GACZ,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,aAAa,GACb,gBAAgB,CACnB,CACF;IACD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,4BAA4B,CAAC;IACnD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA0VD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAqWtE"}
|
|
@@ -3,6 +3,7 @@ import { canonicalSvelteKitPath } from "../vite-plugin/sveltekit-path.js";
|
|
|
3
3
|
import { clearGeneratedSvelteKitRouteFiles, reconcileSvelteKitRouteGitignore } from "../vite-plugin/sveltekit-generator.js";
|
|
4
4
|
import { generateWebModule, isCollectionManifestClass, resolveCollectionItemObject } from "../vite-plugin/web-collections.js";
|
|
5
5
|
import { buildDomainKnowledgeManifest } from "../knowledge.js";
|
|
6
|
+
import { manifestExportCandidates } from "../manifest/package-manifest-exports.js";
|
|
6
7
|
import { loadVerifiedSmrtGenerationSnapshot, serializeSmrtGenerationSnapshot, sha256SmrtGenerationSnapshot } from "../generation-snapshot.js";
|
|
7
8
|
import { resolveFileKnowledgeConfig } from "../knowledge-config.js";
|
|
8
9
|
import { generateClientModule } from "../vite-plugin/generated-client.js";
|
|
@@ -181,9 +182,29 @@ function smrtConsumer(options = {}) {
|
|
|
181
182
|
if (!generationSnapshot) throw new Error("[smrt:consumer] Generation snapshot is not configured");
|
|
182
183
|
return loadVerifiedSmrtGenerationSnapshot(generationSnapshot, projectRoot, "dependencies");
|
|
183
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Resolve the packages to aggregate, and whether that list was asserted by
|
|
187
|
+
* the consumer's config. An explicit list fails closed when a package
|
|
188
|
+
* yields no manifest; a heuristically discovered one only warns.
|
|
189
|
+
*/
|
|
190
|
+
async function resolveConsumerPackages() {
|
|
191
|
+
if (packages.length === 0 && !disableScanning) return {
|
|
192
|
+
names: await discoverSmrtPackages(projectRoot),
|
|
193
|
+
explicit: false
|
|
194
|
+
};
|
|
195
|
+
return {
|
|
196
|
+
names: packages,
|
|
197
|
+
explicit: packages.length > 0
|
|
198
|
+
};
|
|
199
|
+
}
|
|
184
200
|
async function generateConfigTypes(manifest) {
|
|
185
201
|
if (!generateTypes || typesGenerated) return;
|
|
186
|
-
|
|
202
|
+
if (manifest) typeManifest = manifest;
|
|
203
|
+
else if (generationSnapshot) typeManifest = loadGenerationSnapshot();
|
|
204
|
+
else {
|
|
205
|
+
const resolved = await resolveConsumerPackages();
|
|
206
|
+
typeManifest = await aggregateTypeManifests(resolved.names, projectRoot, { explicit: resolved.explicit });
|
|
207
|
+
}
|
|
187
208
|
await generateProjectTypes(typeManifest, typesDir, projectRoot);
|
|
188
209
|
typesGenerated = true;
|
|
189
210
|
}
|
|
@@ -198,8 +219,8 @@ function smrtConsumer(options = {}) {
|
|
|
198
219
|
const previousConsumerRouteRoots = loadConsumerSvelteKitRouteRoots(projectRoot);
|
|
199
220
|
if (consumerSvelteKit) {
|
|
200
221
|
await assertConsumerSvelteKitFormerRouteRootsAreSafe(userConfig, projectRoot, previousConsumerRouteRoots, env);
|
|
201
|
-
const routePackages =
|
|
202
|
-
const routeManifest = generationSnapshot ? loadGenerationSnapshot() : await aggregateTypeManifests(routePackages, projectRoot);
|
|
222
|
+
const routePackages = await resolveConsumerPackages();
|
|
223
|
+
const routeManifest = generationSnapshot ? loadGenerationSnapshot() : await aggregateTypeManifests(routePackages.names, projectRoot, { explicit: routePackages.explicit });
|
|
203
224
|
const hostedManifest = selectConsumerRouteManifest(routeManifest, consumerSvelteKit);
|
|
204
225
|
const routesDir = canonicalConsumerRouteRoot(projectRoot, consumerSvelteKit.routesDir ?? "src/routes/api");
|
|
205
226
|
const reservedRoutePaths = await activeProducerKnowledgeRoutePaths(userConfig, projectRoot, env);
|
|
@@ -259,11 +280,11 @@ function smrtConsumer(options = {}) {
|
|
|
259
280
|
}
|
|
260
281
|
return;
|
|
261
282
|
}
|
|
262
|
-
|
|
263
|
-
|
|
283
|
+
const resolvedPackages = await resolveConsumerPackages();
|
|
284
|
+
smrtPackages = resolvedPackages.names;
|
|
264
285
|
if (smrtPackages.length > 0) {
|
|
265
286
|
console.log(`[smrt:consumer] Found SMRT packages: ${smrtPackages.join(", ")}`);
|
|
266
|
-
typeManifest = await aggregateTypeManifests(smrtPackages, projectRoot);
|
|
287
|
+
typeManifest = await aggregateTypeManifests(smrtPackages, projectRoot, { explicit: resolvedPackages.explicit });
|
|
267
288
|
const agentSurface = producerApi ? await producerApi.resolveKnowledgeAgentSurface() : void 0;
|
|
268
289
|
await saveAggregatedManifest(typeManifest, projectRoot, producerApi?.resolveKnowledgeConfig, agentSurface);
|
|
269
290
|
await generateRegistrationFile(typeManifest, projectRoot);
|
|
@@ -343,66 +364,137 @@ async function discoverSmrtPackages(projectRoot) {
|
|
|
343
364
|
return packages;
|
|
344
365
|
}
|
|
345
366
|
/**
|
|
346
|
-
*
|
|
367
|
+
* Manifest locations probed for a consumed package, in preference order.
|
|
368
|
+
*
|
|
369
|
+
* The package's own `package.json#exports` map comes first: it is the
|
|
370
|
+
* published contract for where the manifest lives, and a package whose build
|
|
371
|
+
* emits outside this framework's conventional layout (for example
|
|
372
|
+
* `"./manifest.json": "./dist/lib/manifest.json"`) is otherwise skipped
|
|
373
|
+
* entirely (issue #2923). The conventional paths remain as a fallback for
|
|
374
|
+
* packages that ship a manifest without exporting a subpath for it.
|
|
375
|
+
*/
|
|
376
|
+
function legacyStaticManifestPath(packageDir) {
|
|
377
|
+
return path.join(packageDir, "dist", "manifest", "static-manifest.js");
|
|
378
|
+
}
|
|
379
|
+
function packageManifestCandidates(packageDir, packageJson) {
|
|
380
|
+
return [
|
|
381
|
+
...manifestExportCandidates(packageDir, packageJson),
|
|
382
|
+
legacyStaticManifestPath(packageDir),
|
|
383
|
+
path.join(packageDir, "dist", "manifest.json"),
|
|
384
|
+
path.join(packageDir, "manifest.json")
|
|
385
|
+
];
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Whether a dependency looks like a SMRT package, for the name-heuristic
|
|
389
|
+
* discovery path only.
|
|
390
|
+
*
|
|
391
|
+
* This decides whether a package this framework knows nothing about is pulled
|
|
392
|
+
* into aggregation at all, so mere existence of a manifest-shaped file is not
|
|
393
|
+
* enough: `manifest.json` at a package root or `dist/manifest.json` is a
|
|
394
|
+
* common artifact of unrelated tooling (Vite build manifests, PWA and
|
|
395
|
+
* extension manifests). A false positive would announce a stranger under
|
|
396
|
+
* "Found SMRT packages", tell the user to publish a manifest for a package
|
|
397
|
+
* that has nothing to do with SMRT, and — for a JS candidate — evaluate that
|
|
398
|
+
* dependency's module inside the build.
|
|
399
|
+
*
|
|
400
|
+
* A JSON candidate is therefore confirmed by parsing it and requiring
|
|
401
|
+
* `moduleType: 'smrt'`, matching what build-time discovery requires. A JS
|
|
402
|
+
* candidate cannot be identified without importing it, which is the thing
|
|
403
|
+
* being avoided, so only the historical `dist/manifest/static-manifest.js`
|
|
404
|
+
* location counts — exactly the probe this function used before #2923.
|
|
347
405
|
*/
|
|
348
406
|
async function hasSmrtManifest(nodeModulesPath, packageName) {
|
|
349
407
|
const packagePath = path.join(nodeModulesPath, packageName);
|
|
350
|
-
const
|
|
351
|
-
|
|
408
|
+
const legacyStaticManifest = legacyStaticManifestPath(packagePath);
|
|
409
|
+
for (const manifestPath of packageManifestCandidates(packagePath)) {
|
|
410
|
+
if (!fs.existsSync(manifestPath)) continue;
|
|
411
|
+
if (!manifestPath.endsWith(".json")) {
|
|
412
|
+
if (manifestPath === legacyStaticManifest) return true;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
if (JSON.parse(fs.readFileSync(manifestPath, "utf-8"))?.moduleType === "smrt") return true;
|
|
417
|
+
} catch {}
|
|
418
|
+
}
|
|
419
|
+
return false;
|
|
352
420
|
}
|
|
353
421
|
/**
|
|
354
|
-
* Aggregate type manifests from multiple packages
|
|
422
|
+
* Aggregate type manifests from multiple packages.
|
|
423
|
+
*
|
|
424
|
+
* A package that yields no usable manifest is never dropped silently
|
|
425
|
+
* (issue #2923): an explicitly listed package fails the build, and a
|
|
426
|
+
* heuristically discovered one warns by name. Silence here surfaces much
|
|
427
|
+
* later as missing tables, routes, and generated types with nothing in the
|
|
428
|
+
* build log to connect them to the package that was skipped.
|
|
429
|
+
*
|
|
430
|
+
* @param packages - Package names to aggregate.
|
|
431
|
+
* @param projectRoot - Consumer project root containing `node_modules`.
|
|
432
|
+
* @param options - `explicit` marks a caller-supplied `packages` list, whose
|
|
433
|
+
* entries are assertions rather than guesses and therefore fail closed.
|
|
355
434
|
*/
|
|
356
|
-
async function aggregateTypeManifests(packages, projectRoot) {
|
|
435
|
+
async function aggregateTypeManifests(packages, projectRoot, options = {}) {
|
|
357
436
|
const aggregatedManifest = {
|
|
358
437
|
version: "1.0.0",
|
|
359
438
|
timestamp: 0,
|
|
360
439
|
smrtDependencies: [...packages],
|
|
361
440
|
objects: {}
|
|
362
441
|
};
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
let packageJson;
|
|
442
|
+
const unresolvedPackages = [];
|
|
443
|
+
for (const packageName of packages) {
|
|
444
|
+
let loadedManifest = false;
|
|
367
445
|
try {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
];
|
|
379
|
-
for (const manifestPath of manifestCandidates) if (fs.existsSync(manifestPath)) {
|
|
380
|
-
let manifest;
|
|
381
|
-
if (manifestPath.endsWith(".js")) {
|
|
382
|
-
const manifestModule = await import(manifestPath);
|
|
383
|
-
manifest = manifestModule.staticManifest || manifestModule.default;
|
|
384
|
-
} else {
|
|
385
|
-
const manifestContent = fs.readFileSync(manifestPath, "utf-8");
|
|
386
|
-
manifest = JSON.parse(manifestContent);
|
|
446
|
+
const packageDir = path.join(projectRoot, "node_modules", packageName);
|
|
447
|
+
const packageJsonPath = path.join(packageDir, "package.json");
|
|
448
|
+
let packageJson;
|
|
449
|
+
try {
|
|
450
|
+
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
|
|
451
|
+
packageJson = JSON.parse(packageJsonContent);
|
|
452
|
+
} catch {
|
|
453
|
+
console.warn(`[smrt:consumer] Could not read package.json for ${packageName}`);
|
|
454
|
+
unresolvedPackages.push(packageName);
|
|
455
|
+
continue;
|
|
387
456
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
457
|
+
const manifestCandidates = packageManifestCandidates(packageDir, packageJson);
|
|
458
|
+
for (const manifestPath of manifestCandidates) if (fs.existsSync(manifestPath)) {
|
|
459
|
+
let manifest;
|
|
460
|
+
try {
|
|
461
|
+
if (manifestPath.endsWith(".js")) {
|
|
462
|
+
const manifestModule = await import(manifestPath);
|
|
463
|
+
manifest = manifestModule.staticManifest || manifestModule.default;
|
|
464
|
+
} else {
|
|
465
|
+
const manifestContent = fs.readFileSync(manifestPath, "utf-8");
|
|
466
|
+
manifest = JSON.parse(manifestContent);
|
|
467
|
+
}
|
|
468
|
+
} catch (error) {
|
|
469
|
+
console.warn(`[smrt:consumer] Could not load manifest candidate ${manifestPath} for ${packageName}; trying the next location:`, error);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (manifest?.objects) {
|
|
473
|
+
console.log(`[smrt:consumer] Loaded manifest from ${packageName} (${Object.keys(manifest.objects).length} objects)`);
|
|
474
|
+
for (const [objectName, objectDef] of Object.entries(manifest.objects)) {
|
|
475
|
+
const def = objectDef;
|
|
476
|
+
aggregatedManifest.objects[objectName] = {
|
|
477
|
+
...def,
|
|
478
|
+
packageName: def.packageName || manifest.packageName || packageName,
|
|
479
|
+
packageVersion: def.packageVersion || manifest.packageVersion || packageJson.version,
|
|
480
|
+
importPath: def.importPath || determineImportPath(packageJson),
|
|
481
|
+
exportName: def.exportName || def.className || objectName,
|
|
482
|
+
collectionExportName: def.collectionExportName || `${def.className || objectName}Collection`
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
loadedManifest = true;
|
|
486
|
+
break;
|
|
400
487
|
}
|
|
401
|
-
break;
|
|
402
488
|
}
|
|
489
|
+
} catch (error) {
|
|
490
|
+
console.warn(`[smrt:consumer] Error loading manifest from ${packageName}:`, error);
|
|
403
491
|
}
|
|
404
|
-
|
|
405
|
-
|
|
492
|
+
if (!loadedManifest) unresolvedPackages.push(packageName);
|
|
493
|
+
}
|
|
494
|
+
if (unresolvedPackages.length > 0) {
|
|
495
|
+
const named = unresolvedPackages.join(", ");
|
|
496
|
+
if (options.explicit) throw new Error(`[smrt:consumer] No SMRT manifest could be resolved for ${named}. Listed packages must publish a manifest through package.json#exports ("./manifest.json") or at dist/manifest.json, dist/manifest/static-manifest.js, or manifest.json. Build the package, or remove it from smrtConsumer({ packages }).`);
|
|
497
|
+
console.warn(`[smrt:consumer] No SMRT manifest could be resolved for ${named}; contributing 0 objects. If the package provides SMRT objects, publish its manifest through package.json#exports ("./manifest.json").`);
|
|
406
498
|
}
|
|
407
499
|
return aggregatedManifest;
|
|
408
500
|
}
|