@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.
- package/README.md +49 -0
- package/dist/consumer-plugin/index.d.ts +19 -4
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +204 -6
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +13 -7
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +13 -7
- package/dist/scanner/manifest-generator.d.ts +39 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +79 -12
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/smrt-knowledge.json +3 -3
- package/dist/vite-plugin/changes-route.d.ts.map +1 -1
- package/dist/vite-plugin/changes-route.js +4 -3
- package/dist/vite-plugin/changes-route.js.map +1 -1
- package/dist/vite-plugin/dev-plane-route.d.ts +2 -0
- package/dist/vite-plugin/dev-plane-route.d.ts.map +1 -1
- package/dist/vite-plugin/dev-plane-route.js +8 -5
- package/dist/vite-plugin/dev-plane-route.js.map +1 -1
- package/dist/vite-plugin/events-route.d.ts.map +1 -1
- package/dist/vite-plugin/events-route.js +4 -3
- package/dist/vite-plugin/events-route.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +25 -40
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/resources-route.d.ts +6 -0
- package/dist/vite-plugin/resources-route.d.ts.map +1 -1
- package/dist/vite-plugin/resources-route.js +30 -7
- package/dist/vite-plugin/resources-route.js.map +1 -1
- package/dist/vite-plugin/sveltekit-config-import.d.ts +19 -0
- package/dist/vite-plugin/sveltekit-config-import.d.ts.map +1 -0
- package/dist/vite-plugin/sveltekit-config-import.js +36 -0
- package/dist/vite-plugin/sveltekit-config-import.js.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts +33 -2
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +181 -65
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/sveltekit-path.d.ts +8 -0
- package/dist/vite-plugin/sveltekit-path.d.ts.map +1 -0
- package/dist/vite-plugin/sveltekit-path.js +25 -0
- package/dist/vite-plugin/sveltekit-path.js.map +1 -0
- package/dist/vite-plugin/sveltekit-route-coordinator.d.ts +67 -0
- package/dist/vite-plugin/sveltekit-route-coordinator.d.ts.map +1 -0
- package/dist/vite-plugin/sveltekit-route-coordinator.js +321 -0
- package/dist/vite-plugin/sveltekit-route-coordinator.js.map +1 -0
- package/dist/vite-plugin/sync-apply-route.d.ts +1 -1
- package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
- package/dist/vite-plugin/sync-apply-route.js +4 -3
- package/dist/vite-plugin/sync-apply-route.js.map +1 -1
- 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
|
-
/**
|
|
20
|
-
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
184
|
+
const plugin = {
|
|
38
185
|
name: "smrt-consumer",
|
|
39
|
-
|
|
40
|
-
|
|
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 {
|