@happyvertical/smrt-core 0.49.5 → 0.49.6

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 (54) hide show
  1. package/README.md +49 -0
  2. package/dist/consumer-plugin/index.d.ts +19 -4
  3. package/dist/consumer-plugin/index.d.ts.map +1 -1
  4. package/dist/consumer-plugin/index.js +204 -6
  5. package/dist/consumer-plugin/index.js.map +1 -1
  6. package/dist/manifest/static-manifest.d.ts.map +1 -1
  7. package/dist/manifest/static-manifest.js +13 -7
  8. package/dist/manifest/static-manifest.js.map +1 -1
  9. package/dist/manifest/store.js +1 -1
  10. package/dist/manifest/store.js.map +1 -1
  11. package/dist/manifest.json +13 -7
  12. package/dist/scanner/manifest-generator.d.ts +39 -0
  13. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  14. package/dist/scanner/manifest-generator.js +79 -12
  15. package/dist/scanner/manifest-generator.js.map +1 -1
  16. package/dist/smrt-knowledge.json +3 -3
  17. package/dist/vite-plugin/changes-route.d.ts.map +1 -1
  18. package/dist/vite-plugin/changes-route.js +4 -3
  19. package/dist/vite-plugin/changes-route.js.map +1 -1
  20. package/dist/vite-plugin/dev-plane-route.d.ts +2 -0
  21. package/dist/vite-plugin/dev-plane-route.d.ts.map +1 -1
  22. package/dist/vite-plugin/dev-plane-route.js +8 -5
  23. package/dist/vite-plugin/dev-plane-route.js.map +1 -1
  24. package/dist/vite-plugin/events-route.d.ts.map +1 -1
  25. package/dist/vite-plugin/events-route.js +4 -3
  26. package/dist/vite-plugin/events-route.js.map +1 -1
  27. package/dist/vite-plugin/index.d.ts.map +1 -1
  28. package/dist/vite-plugin/index.js +25 -40
  29. package/dist/vite-plugin/index.js.map +1 -1
  30. package/dist/vite-plugin/resources-route.d.ts +6 -0
  31. package/dist/vite-plugin/resources-route.d.ts.map +1 -1
  32. package/dist/vite-plugin/resources-route.js +30 -7
  33. package/dist/vite-plugin/resources-route.js.map +1 -1
  34. package/dist/vite-plugin/sveltekit-config-import.d.ts +19 -0
  35. package/dist/vite-plugin/sveltekit-config-import.d.ts.map +1 -0
  36. package/dist/vite-plugin/sveltekit-config-import.js +36 -0
  37. package/dist/vite-plugin/sveltekit-config-import.js.map +1 -0
  38. package/dist/vite-plugin/sveltekit-generator.d.ts +33 -2
  39. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  40. package/dist/vite-plugin/sveltekit-generator.js +181 -65
  41. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  42. package/dist/vite-plugin/sveltekit-path.d.ts +8 -0
  43. package/dist/vite-plugin/sveltekit-path.d.ts.map +1 -0
  44. package/dist/vite-plugin/sveltekit-path.js +25 -0
  45. package/dist/vite-plugin/sveltekit-path.js.map +1 -0
  46. package/dist/vite-plugin/sveltekit-route-coordinator.d.ts +67 -0
  47. package/dist/vite-plugin/sveltekit-route-coordinator.d.ts.map +1 -0
  48. package/dist/vite-plugin/sveltekit-route-coordinator.js +321 -0
  49. package/dist/vite-plugin/sveltekit-route-coordinator.js.map +1 -0
  50. package/dist/vite-plugin/sync-apply-route.d.ts +1 -1
  51. package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
  52. package/dist/vite-plugin/sync-apply-route.js +4 -3
  53. package/dist/vite-plugin/sync-apply-route.js.map +1 -1
  54. package/package.json +4 -4
package/README.md CHANGED
@@ -213,6 +213,55 @@ Enable SvelteKit route generation with `svelteKit: { enabled: true }`. Its
213
213
  default output directory is `src/routes/api`; set `svelteKit.routesDir` when
214
214
  your application uses a different route root.
215
215
 
216
+ A consumer can host selected dependency models with the same generator. This is
217
+ an explicit HTTP boundary that is separate from the broader `packages`
218
+ registration inventory. Name exact provider-qualified object references; a
219
+ simple class name, an unknown object, or an empty list fails the build before
220
+ any generated route is replaced:
221
+
222
+ ```ts
223
+ smrtConsumer({
224
+ packages: runtimeProviderPackages,
225
+ svelteKit: {
226
+ objects: ['@acme/widgets:Widget'],
227
+ routesDir: 'src/routes/api',
228
+ },
229
+ });
230
+ ```
231
+
232
+ `projectRoot` selects one artifact root for consumer route generation,
233
+ registration, manifests, and types; it defaults to the current working
234
+ directory. It remains the consumer artifact root when a Vite config declares a
235
+ different `root`; pass `projectRoot` explicitly when consumer artifacts should
236
+ share that Vite root or belong elsewhere.
237
+
238
+ Only the listed objects are passed to route generation, so another API model in
239
+ the same provider remains absent until it is named. `api: false` and an empty
240
+ `api.include` still suppress handlers, and generated auth, writable-field, and
241
+ tenant safeguards are unchanged. `svelteKit: true` remains a legacy consumer
242
+ integration flag and does not host dependency CRUD routes. Consumer-hosted
243
+ `_changes`, `_events`, and `_resources` routes are disabled unless their
244
+ existing option is explicitly enabled; `sync/apply` is generated when a selected model exposes a mutating API
245
+ action.
246
+
247
+ When `smrtPlugin()` and `smrtConsumer()` generate routes in one SvelteKit
248
+ application, they may use the same canonical `routesDir` and share one
249
+ generated surface, or use disjoint directories. Nested directories are
250
+ rejected before files are changed because a parent generated-root cleanup would
251
+ otherwise own and remove the child surface.
252
+ Canonical ownership resolves symlink aliases through the nearest existing
253
+ directory, so paths that name the same generated directory share one surface.
254
+ When separate artifact roots resolve to that same route directory, their
255
+ resolved object and config outputs must also agree; incompatible settings fail
256
+ before generated handlers or managed ownership records change.
257
+
258
+ The consumer keeps a private managed-root inventory under `.smrt` after a
259
+ successful explicit hosting plan is validated. Changing its `routesDir`,
260
+ setting `svelteKit: false`, returning to legacy `svelteKit: true`, or omitting
261
+ the option reconciles only consumer-managed generated handlers before SvelteKit
262
+ inventories routes. Handwritten handlers and current producer-owned routes are
263
+ preserved.
264
+
216
265
  The plugin records only the concrete `+server.ts` files it generated in a
217
266
  bounded `.gitignore` block. Handwritten handlers below `routesDir` remain
218
267
  visible to Git, including routes that live beside generated resource handlers.
@@ -1,6 +1,15 @@
1
1
  import { Plugin } from 'vite';
2
2
  import { SmrtGenerationSnapshotOptions } from '../generation-snapshot.js';
3
+ import { SvelteKitOptions } from '../vite-plugin/sveltekit-generator.js';
3
4
  export { loadVerifiedSmrtGenerationSnapshot, type SerializeSmrtGenerationSnapshotOptions, type SmrtGenerationSnapshotArtifact, type SmrtGenerationSnapshotOptions, type SmrtGenerationSnapshotView, serializeSmrtGenerationSnapshot, sha256SmrtGenerationSnapshot, } from '../generation-snapshot.js';
5
+ /**
6
+ * SvelteKit route-hosting options for dependency models. Unlike `packages`,
7
+ * `objects` is an HTTP exposure boundary: every entry must be an exact,
8
+ * provider-qualified manifest key (for example, `@acme/widgets:Widget`).
9
+ */
10
+ export interface SmrtConsumerSvelteKitOptions extends Partial<Pick<SvelteKitOptions, 'routesDir' | 'configPath' | 'configFileName' | 'kebabRoutes' | 'changesRoute' | 'eventsRoute' | 'resourcesRoute'>> {
11
+ objects: readonly string[];
12
+ }
4
13
  export interface SmrtConsumerOptions {
5
14
  /** SMRT packages to scan (e.g., ['@my-org/products', '@my-org/content']) */
6
15
  packages?: string[];
@@ -8,7 +17,7 @@ export interface SmrtConsumerOptions {
8
17
  generateTypes?: boolean;
9
18
  /** Output directory for generated types */
10
19
  typesDir?: string;
11
- /** Project root path */
20
+ /** Project root path (defaults to the current working directory) */
12
21
  projectRoot?: string;
13
22
  /**
14
23
  * Reuse an immutable, verified aggregated manifest instead of discovering
@@ -16,11 +25,17 @@ export interface SmrtConsumerOptions {
16
25
  * types still consume the verified manifest.
17
26
  */
18
27
  generationSnapshot?: SmrtGenerationSnapshotOptions;
19
- /** SvelteKit integration mode */
20
- svelteKit?: boolean;
28
+ /**
29
+ * Consumer SvelteKit integration. `true` retains the historical compatibility
30
+ * mode and does not generate dependency routes. Route hosting requires an
31
+ * explicit, provider-qualified object allowlist.
32
+ */
33
+ svelteKit?: boolean | SmrtConsumerSvelteKitOptions;
21
34
  /**
22
35
  * Apply kebab-case to generated custom-method URL segments. This must match
23
- * the producer plugin's `svelteKit.kebabRoutes` setting.
36
+ * the producer plugin's `svelteKit.kebabRoutes` setting. When explicit
37
+ * consumer SvelteKit hosting is configured, its `kebabRoutes` value takes
38
+ * precedence, including an explicit `false`.
24
39
  */
25
40
  kebabRoutes?: boolean;
26
41
  /** Use static types only (for federation builds) */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAWnC,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;AAoDnC,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,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAkBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAmLtE"}
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;AAWnC,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,CAwTtE"}
@@ -1,9 +1,12 @@
1
1
  import "../scanner/types.js";
2
- import { generateWebModule } from "../vite-plugin/web-collections.js";
2
+ import { canonicalSvelteKitPath } from "../vite-plugin/sveltekit-path.js";
3
+ import { clearGeneratedSvelteKitRouteFiles, reconcileSvelteKitRouteGitignore } from "../vite-plugin/sveltekit-generator.js";
4
+ import { generateWebModule, isCollectionManifestClass, resolveCollectionItemObject } from "../vite-plugin/web-collections.js";
3
5
  import { buildDomainKnowledgeManifest } from "../knowledge.js";
4
6
  import { loadVerifiedSmrtGenerationSnapshot, serializeSmrtGenerationSnapshot, sha256SmrtGenerationSnapshot } from "../generation-snapshot.js";
5
7
  import { resolveFileKnowledgeConfig } from "../knowledge-config.js";
6
8
  import { generateClientModule } from "../vite-plugin/generated-client.js";
9
+ import { activeProducerKnowledgeRoutePaths, activeSvelteKitRouteParticipants, assertNoSvelteKitRouteRootSymlinkConflict, assertSvelteKitRouteCoordinationComplete, contributeSvelteKitRoutes, expectedSvelteKitRouteOwners, markSvelteKitRouteParticipant, producerKnowledgeRoutePaths, revokeSvelteKitRoutes } from "../vite-plugin/sveltekit-route-coordinator.js";
7
10
  import { generateDeclarations } from "../prebuild/index.js";
8
11
  import { publishArtifactFiles } from "./artifact-publication.js";
9
12
  import * as fs from "node:fs";
@@ -21,25 +24,217 @@ var VIRTUAL_MODULES = {
21
24
  "@smrt/manifest": "smrt-consumer:manifest",
22
25
  "@smrt/web": "smrt-consumer:web"
23
26
  };
27
+ var CONSUMER_SVELTEKIT_ROUTES_ARTIFACT_VERSION = 1;
28
+ var CONSUMER_SVELTEKIT_ROUTES_ARTIFACT = "consumer-sveltekit-routes.json";
29
+ function consumerSvelteKitRoutesArtifactPath(projectRoot) {
30
+ return path.join(projectRoot, ".smrt", CONSUMER_SVELTEKIT_ROUTES_ARTIFACT);
31
+ }
32
+ /** Persist only project-relative consumer route roots, never an output file list. */
33
+ function canonicalConsumerRouteRoot(projectRoot, routesDir) {
34
+ const root = path.resolve(projectRoot);
35
+ const relative = path.relative(root, path.resolve(root, routesDir));
36
+ if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`[smrt:consumer] svelteKit.routesDir must be a project-relative subdirectory (received ${JSON.stringify(routesDir)})`);
37
+ return relative.split(path.sep).join("/");
38
+ }
39
+ function loadConsumerSvelteKitRouteRoots(projectRoot) {
40
+ const artifactPath = consumerSvelteKitRoutesArtifactPath(projectRoot);
41
+ if (!fs.existsSync(artifactPath)) return [];
42
+ let parsed;
43
+ try {
44
+ parsed = JSON.parse(fs.readFileSync(artifactPath, "utf-8"));
45
+ } catch {
46
+ throw new Error(`[smrt:consumer] Cannot read ${CONSUMER_SVELTEKIT_ROUTES_ARTIFACT}; refusing to leave hosted routes unreconciled`);
47
+ }
48
+ if (parsed?.version !== CONSUMER_SVELTEKIT_ROUTES_ARTIFACT_VERSION || !Array.isArray(parsed.routesDir) || parsed.routesDir.some((routesDir) => typeof routesDir !== "string")) throw new Error(`[smrt:consumer] Invalid ${CONSUMER_SVELTEKIT_ROUTES_ARTIFACT}; refusing to leave hosted routes unreconciled`);
49
+ return [...new Set(parsed.routesDir)].map((routesDir) => canonicalConsumerRouteRoot(projectRoot, routesDir));
50
+ }
51
+ function publishConsumerSvelteKitRouteRoots(projectRoot, routesDir) {
52
+ const artifactPath = consumerSvelteKitRoutesArtifactPath(projectRoot);
53
+ fs.mkdirSync(path.dirname(artifactPath), { recursive: true });
54
+ publishArtifactFiles([{
55
+ path: artifactPath,
56
+ content: JSON.stringify({
57
+ version: CONSUMER_SVELTEKIT_ROUTES_ARTIFACT_VERSION,
58
+ routesDir: [...new Set(routesDir)].sort()
59
+ }, null, 2)
60
+ }]);
61
+ }
62
+ function removeConsumerSvelteKitRouteRoots(projectRoot) {
63
+ const artifactPath = consumerSvelteKitRoutesArtifactPath(projectRoot);
64
+ if (fs.existsSync(artifactPath)) fs.unlinkSync(artifactPath);
65
+ }
66
+ /**
67
+ * A hosting-to-hosting move can replace one configured root with another in a
68
+ * fresh lifecycle. Validate every durable former root before the new target
69
+ * journals or clears anything, otherwise a rejected move could alter the
70
+ * prior generated surface before reconciliation notices the conflict.
71
+ */
72
+ async function assertConsumerSvelteKitFormerRouteRootsAreSafe(userConfig, projectRoot, routesDir, env) {
73
+ if (routesDir.length === 0) return;
74
+ const activeRoots = (await activeSvelteKitRouteParticipants(userConfig, projectRoot, env)).map((participant) => participant.routesDir);
75
+ for (const priorRoutesDir of routesDir) assertNoSvelteKitRouteRootSymlinkConflict(canonicalSvelteKitPath(path.resolve(projectRoot, priorRoutesDir)), activeRoots);
76
+ }
77
+ async function reconcileConsumerSvelteKitRouteRoots(lifecycle, userConfig, projectRoot, routesDir, afterReconciled, env) {
78
+ const remaining = new Set(routesDir);
79
+ const reconcileOne = (routesDir) => {
80
+ remaining.delete(routesDir);
81
+ if (remaining.size === 0) afterReconciled();
82
+ };
83
+ if (remaining.size === 0) {
84
+ afterReconciled();
85
+ return;
86
+ }
87
+ for (const routesDir of [...remaining]) {
88
+ const routeRoot = canonicalSvelteKitPath(path.resolve(projectRoot, routesDir));
89
+ const activeParticipants = await activeSvelteKitRouteParticipants(userConfig, projectRoot, env);
90
+ assertNoSvelteKitRouteRootSymlinkConflict(routeRoot, activeParticipants.map((participant) => participant.routesDir));
91
+ if (activeParticipants.find((participant) => participant.owner === "consumer" && (routeRoot === participant.routesDir || routeRoot.startsWith(`${participant.routesDir}${path.sep}`)))) {
92
+ reconcileOne(routesDir);
93
+ continue;
94
+ }
95
+ const containingProducer = activeParticipants.find((participant) => participant.owner === "producer" && (routeRoot === participant.routesDir || routeRoot.startsWith(`${participant.routesDir}${path.sep}`)));
96
+ if (containingProducer) {
97
+ await revokeSvelteKitRoutes(lifecycle, await expectedSvelteKitRouteOwners(userConfig, containingProducer.projectRoot, containingProducer.routesDir, env), containingProducer.projectRoot, containingProducer.routesDir, () => reconcileOne(routesDir));
98
+ continue;
99
+ }
100
+ const owners = await expectedSvelteKitRouteOwners(userConfig, projectRoot, routesDir, env);
101
+ if (owners.includes("producer")) {
102
+ await revokeSvelteKitRoutes(lifecycle, owners, projectRoot, routesDir, () => reconcileOne(routesDir));
103
+ continue;
104
+ }
105
+ clearGeneratedSvelteKitRouteFiles(routeRoot, producerKnowledgeRoutePaths(lifecycle), new Set(activeParticipants.map((participant) => participant.routesDir).filter((activeRoot) => activeRoot !== routeRoot)));
106
+ reconcileSvelteKitRouteGitignore(projectRoot, routesDir);
107
+ reconcileOne(routesDir);
108
+ }
109
+ }
110
+ function consumerRouteOptions(value) {
111
+ if (!value || value === true) return void 0;
112
+ if (!Array.isArray(value.objects) || value.objects.length === 0) throw new Error("[smrt:consumer] svelteKit.objects must list at least one provider-qualified object reference");
113
+ for (const objectRef of value.objects) if (typeof objectRef !== "string" || !objectRef.includes(":")) throw new Error(`[smrt:consumer] svelteKit.objects entries must be provider-qualified (received ${JSON.stringify(objectRef)})`);
114
+ return value;
115
+ }
116
+ function consumerUtilityOption(value) {
117
+ return value?.enabled === true ? value : { enabled: false };
118
+ }
119
+ function consumerObjectRef(manifestKey, objectDef) {
120
+ if (manifestKey.includes(":")) return manifestKey;
121
+ if (objectDef.qualifiedName?.includes(":")) return objectDef.qualifiedName;
122
+ if (objectDef.packageName && objectDef.className) return `${objectDef.packageName}:${objectDef.className}`;
123
+ }
124
+ /**
125
+ * Select exactly the dependency objects that a consumer explicitly hosts.
126
+ * Validation completes before the generator clears its managed files, so an
127
+ * invalid deployment cannot erase a previously generated route surface.
128
+ */
129
+ function selectConsumerRouteManifest(manifest, options) {
130
+ const entriesByRef = /* @__PURE__ */ new Map();
131
+ for (const [manifestKey, objectDef] of Object.entries(manifest.objects)) {
132
+ const objectRef = consumerObjectRef(manifestKey, objectDef);
133
+ if (objectRef) entriesByRef.set(objectRef, [manifestKey, objectDef]);
134
+ }
135
+ const selected = /* @__PURE__ */ new Set();
136
+ const objects = {};
137
+ for (const objectRef of options.objects) {
138
+ const entry = entriesByRef.get(objectRef);
139
+ if (!entry) throw new Error(`[smrt:consumer] svelteKit.objects references unknown dependency object ${JSON.stringify(objectRef)}`);
140
+ if (selected.has(objectRef)) throw new Error(`[smrt:consumer] svelteKit.objects contains duplicate object ${JSON.stringify(objectRef)}`);
141
+ selected.add(objectRef);
142
+ const [, objectDef] = entry;
143
+ objects[objectRef] = {
144
+ ...objectDef,
145
+ qualifiedName: objectRef
146
+ };
147
+ }
148
+ const sourceManifest = manifest;
149
+ for (const [manifestKey, objectDef] of Object.entries(manifest.objects)) {
150
+ const candidate = objectDef;
151
+ if (!isCollectionManifestClass(sourceManifest, candidate)) continue;
152
+ const item = resolveCollectionItemObject(sourceManifest, candidate);
153
+ const itemEntry = item ? Object.entries(manifest.objects).find(([, value]) => value === item) : void 0;
154
+ const itemRef = itemEntry ? consumerObjectRef(itemEntry[0], itemEntry[1]) : void 0;
155
+ if (!itemRef || !selected.has(itemRef)) continue;
156
+ const collectionRef = consumerObjectRef(manifestKey, objectDef);
157
+ if (collectionRef) objects[collectionRef] = {
158
+ ...objectDef,
159
+ qualifiedName: collectionRef
160
+ };
161
+ }
162
+ return {
163
+ ...manifest,
164
+ smrtDependencies: [...new Set(Object.values(manifest.objects).map((objectDef) => objectDef.packageName).filter((packageName) => typeof packageName === "string" && packageName !== manifest.packageName))].sort(),
165
+ objects
166
+ };
167
+ }
24
168
  /**
25
169
  * Consumer plugin for projects that use SMRT packages
26
170
  */
27
171
  function smrtConsumer(options = {}) {
28
172
  const { packages = [], generateTypes = true, typesDir = "src/types/smrt-generated", projectRoot = process.cwd(), generationSnapshot, disableScanning = false, kebabRoutes = false } = options;
173
+ const consumerSvelteKit = consumerRouteOptions(options.svelteKit);
174
+ const effectiveKebabRoutes = consumerSvelteKit?.kebabRoutes ?? kebabRoutes;
29
175
  let smrtPackages = [];
30
176
  let typeManifest = null;
31
177
  let typesGenerated = false;
32
178
  let producerApi;
179
+ let routeLifecycleConfig;
33
180
  function loadGenerationSnapshot() {
34
181
  if (!generationSnapshot) throw new Error("[smrt:consumer] Generation snapshot is not configured");
35
182
  return loadVerifiedSmrtGenerationSnapshot(generationSnapshot, projectRoot, "dependencies");
36
183
  }
37
- return {
184
+ const plugin = {
38
185
  name: "smrt-consumer",
39
- config() {
40
- return { build: { rollupOptions: { external: [/\.node$/] } } };
186
+ enforce: "pre",
187
+ config: {
188
+ order: "pre",
189
+ async handler(userConfig, env) {
190
+ const routeLifecycle = env ?? userConfig;
191
+ routeLifecycleConfig = routeLifecycle;
192
+ const previousConsumerRouteRoots = loadConsumerSvelteKitRouteRoots(projectRoot);
193
+ if (consumerSvelteKit) {
194
+ await assertConsumerSvelteKitFormerRouteRootsAreSafe(userConfig, projectRoot, previousConsumerRouteRoots, env);
195
+ const routePackages = packages.length === 0 && !disableScanning ? await discoverSmrtPackages(projectRoot) : packages;
196
+ const routeManifest = generationSnapshot ? loadGenerationSnapshot() : await aggregateTypeManifests(routePackages, projectRoot);
197
+ const hostedManifest = selectConsumerRouteManifest(routeManifest, consumerSvelteKit);
198
+ const routesDir = canonicalConsumerRouteRoot(projectRoot, consumerSvelteKit.routesDir ?? "src/routes/api");
199
+ const reservedRoutePaths = await activeProducerKnowledgeRoutePaths(userConfig, projectRoot, env);
200
+ const routeOptions = {
201
+ enabled: true,
202
+ routesDir,
203
+ objectsDir: "src/lib/objects",
204
+ configPath: consumerSvelteKit.configPath ?? "src/lib/server",
205
+ configFileName: consumerSvelteKit.configFileName ?? "smrt.ts",
206
+ kebabRoutes: effectiveKebabRoutes,
207
+ changesRoute: consumerUtilityOption(consumerSvelteKit.changesRoute),
208
+ eventsRoute: consumerUtilityOption(consumerSvelteKit.eventsRoute),
209
+ resourcesRoute: consumerUtilityOption(consumerSvelteKit.resourcesRoute),
210
+ rejectRouteCollisions: true
211
+ };
212
+ let ownershipJournaled = false;
213
+ let reconciliationScheduled = false;
214
+ await contributeSvelteKitRoutes(routeLifecycle, await expectedSvelteKitRouteOwners(userConfig, projectRoot, routeOptions.routesDir, env), projectRoot, {
215
+ owner: "consumer",
216
+ routeManifest: hostedManifest,
217
+ semanticManifest: routeManifest,
218
+ options: routeOptions,
219
+ reservedRoutePaths,
220
+ beforeCleanup: () => {
221
+ if (ownershipJournaled) return;
222
+ ownershipJournaled = true;
223
+ publishConsumerSvelteKitRouteRoots(projectRoot, [...previousConsumerRouteRoots, routesDir]);
224
+ },
225
+ afterGenerate: async () => {
226
+ if (reconciliationScheduled) return;
227
+ reconciliationScheduled = true;
228
+ const priorRoots = previousConsumerRouteRoots.filter((previousRoot) => previousRoot !== routesDir);
229
+ await reconcileConsumerSvelteKitRouteRoots(routeLifecycle, userConfig, projectRoot, priorRoots, () => publishConsumerSvelteKitRouteRoots(projectRoot, [routesDir]), env);
230
+ }
231
+ });
232
+ } else if (previousConsumerRouteRoots.length > 0) await reconcileConsumerSvelteKitRouteRoots(routeLifecycle, userConfig, projectRoot, previousConsumerRouteRoots, () => removeConsumerSvelteKitRouteRoots(projectRoot), env);
233
+ return { build: { rollupOptions: { external: [/\.node$/] } } };
234
+ }
41
235
  },
42
236
  configResolved(resolvedConfig) {
237
+ if (consumerSvelteKit && routeLifecycleConfig) assertSvelteKitRouteCoordinationComplete(routeLifecycleConfig);
43
238
  producerApi = (resolvedConfig.plugins ?? []).find((plugin) => plugin?.name === "smrt-auto-service")?.api;
44
239
  },
45
240
  async buildStart() {
@@ -94,15 +289,17 @@ function smrtConsumer(options = {}) {
94
289
  };
95
290
  switch (cleanId) {
96
291
  case "smrt-consumer:routes": return generateFallbackRoutesModule();
97
- case "smrt-consumer:client": return generateFallbackClientModule(typeManifest, { kebabRoutes });
292
+ case "smrt-consumer:client": return generateFallbackClientModule(typeManifest, { kebabRoutes: effectiveKebabRoutes });
98
293
  case "smrt-consumer:mcp": return generateFallbackMcpModule();
99
294
  case "smrt-consumer:types": return generateFallbackTypesModule(typeManifest);
100
295
  case "smrt-consumer:manifest": return generateFallbackManifestModule(typeManifest);
101
- case "smrt-consumer:web": return generateWebModule(typeManifest, { kebabRoutes });
296
+ case "smrt-consumer:web": return generateWebModule(typeManifest, { kebabRoutes: effectiveKebabRoutes });
102
297
  default: return null;
103
298
  }
104
299
  }
105
300
  };
301
+ markSvelteKitRouteParticipant(plugin, "consumer", Boolean(consumerSvelteKit), consumerSvelteKit?.routesDir ?? "src/routes/api", void 0, () => projectRoot);
302
+ return plugin;
106
303
  }
107
304
  /**
108
305
  * Discover SMRT packages from a consumer app's dependencies.
@@ -150,6 +347,7 @@ async function aggregateTypeManifests(packages, projectRoot) {
150
347
  const aggregatedManifest = {
151
348
  version: "1.0.0",
152
349
  timestamp: 0,
350
+ smrtDependencies: [...packages],
153
351
  objects: {}
154
352
  };
155
353
  for (const packageName of packages) try {