@zodmire/core 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026-present Vercel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,91 @@
1
+ //#region packages/core/artifacts.ts
2
+ function safeDataName(value) {
3
+ return value.replace(/[\\/]+/g, "__").replace(/[^A-Za-z0-9_.-]+/g, "_");
4
+ }
5
+ function createGeneratedArtifact(logicalPath, content, ownership) {
6
+ return {
7
+ logicalPath,
8
+ dataName: safeDataName(logicalPath),
9
+ content,
10
+ owned: true,
11
+ ownershipScope: ownership?.ownershipScope,
12
+ scopeKey: ownership?.scopeKey
13
+ };
14
+ }
15
+ function withArtifactOwnership(artifact, ownership) {
16
+ return {
17
+ ...artifact,
18
+ ownershipScope: ownership.ownershipScope,
19
+ scopeKey: ownership.scopeKey
20
+ };
21
+ }
22
+ function mergeGeneratedArtifacts(...groups) {
23
+ const mergedArtifacts = [];
24
+ const artifactsByLogicalPath = /* @__PURE__ */ new Map();
25
+ for (const artifact of groups.flat()) {
26
+ const existingArtifact = artifactsByLogicalPath.get(artifact.logicalPath);
27
+ if (existingArtifact === void 0) {
28
+ artifactsByLogicalPath.set(artifact.logicalPath, artifact);
29
+ mergedArtifacts.push(artifact);
30
+ continue;
31
+ }
32
+ if (existingArtifact.dataName !== artifact.dataName || existingArtifact.content !== artifact.content || existingArtifact.ownershipScope !== artifact.ownershipScope || existingArtifact.scopeKey !== artifact.scopeKey) throw new Error(`Generated artifact logical path collision: ${artifact.logicalPath}`);
33
+ }
34
+ return mergedArtifacts;
35
+ }
36
+ function sharedArtifactOwnership(scopeKey = "shared") {
37
+ return {
38
+ ownershipScope: "shared-owned",
39
+ scopeKey
40
+ };
41
+ }
42
+ function sliceArtifactOwnership(scopeKey) {
43
+ return {
44
+ ownershipScope: "slice-owned",
45
+ scopeKey
46
+ };
47
+ }
48
+ function contextArtifactOwnership(scopeKey) {
49
+ return {
50
+ ownershipScope: "context-owned",
51
+ scopeKey
52
+ };
53
+ }
54
+ function applyOwnershipIfMissing(artifacts, ownership) {
55
+ return artifacts.map((artifact) => artifact.ownershipScope !== void 0 && artifact.scopeKey !== void 0 ? artifact : withArtifactOwnership(artifact, ownership));
56
+ }
57
+ function inferArtifactOwnership(logicalPath, defaultSliceScopeKey = "default") {
58
+ if (logicalPath.startsWith("lib/")) return sharedArtifactOwnership("lib");
59
+ if (logicalPath.startsWith("infrastructure/di/")) return sharedArtifactOwnership("composition");
60
+ if (logicalPath.startsWith("presentation/event-handlers/")) return sharedArtifactOwnership("composition");
61
+ if (logicalPath === "presentation/trpc/router.ts" || logicalPath === "presentation/trpc/context.ts") return sharedArtifactOwnership("composition");
62
+ if (logicalPath.startsWith("core/ports/messaging/")) return sharedArtifactOwnership("composition");
63
+ if (logicalPath.startsWith("infrastructure/unit-of-work/")) return sharedArtifactOwnership("composition");
64
+ if (logicalPath.startsWith("infrastructure/outbox/")) return sharedArtifactOwnership("composition");
65
+ if (logicalPath.startsWith("core/shared-kernel/") || logicalPath.startsWith("core/ports/")) return sharedArtifactOwnership();
66
+ const contextMatch = logicalPath.match(/^core\/contexts\/(.+?)\/(?:application|domain|tests)\//);
67
+ if (contextMatch) return sliceArtifactOwnership(contextMatch[1] ?? defaultSliceScopeKey);
68
+ if (logicalPath.match(/^infrastructure\/persistence\/[^/]+\/tables\.ts$/)) {
69
+ const ctx = logicalPath.split("/")[2];
70
+ return sliceArtifactOwnership(ctx ?? defaultSliceScopeKey);
71
+ }
72
+ if (logicalPath.match(/^infrastructure\/view-models\/[^/]+\/tables\.ts$/)) {
73
+ const ctx = logicalPath.split("/")[2];
74
+ return sliceArtifactOwnership(ctx ?? defaultSliceScopeKey);
75
+ }
76
+ const repositoryMatch = logicalPath.match(/^infrastructure\/.*\/repositories\/(.+)\/[^/]+$/);
77
+ if (repositoryMatch) return sliceArtifactOwnership(repositoryMatch[1] ?? defaultSliceScopeKey);
78
+ const routerMatch = logicalPath.match(/^presentation\/.*\/routers\/([^.]+)\.router\.ts$/);
79
+ if (routerMatch) return sliceArtifactOwnership(routerMatch[1] ?? defaultSliceScopeKey);
80
+ return sliceArtifactOwnership(defaultSliceScopeKey);
81
+ }
82
+ function resolveArtifactOwnership(artifact, defaultSliceScopeKey = "default") {
83
+ if (artifact.ownershipScope && artifact.scopeKey) return {
84
+ ownershipScope: artifact.ownershipScope,
85
+ scopeKey: artifact.scopeKey
86
+ };
87
+ return inferArtifactOwnership(artifact.logicalPath, defaultSliceScopeKey);
88
+ }
89
+
90
+ //#endregion
91
+ export { mergeGeneratedArtifacts as a, sliceArtifactOwnership as c, inferArtifactOwnership as i, withArtifactOwnership as l, contextArtifactOwnership as n, resolveArtifactOwnership as o, createGeneratedArtifact as r, sharedArtifactOwnership as s, applyOwnershipIfMissing as t };
@@ -0,0 +1,25 @@
1
+ //#region packages/core/artifacts.d.ts
2
+ type OwnershipScope = "shared-owned" | "slice-owned" | "context-owned";
3
+ type ArtifactOwnership = {
4
+ ownershipScope: OwnershipScope;
5
+ scopeKey: string;
6
+ };
7
+ type GeneratedArtifact = {
8
+ logicalPath: string;
9
+ dataName: string;
10
+ content: string;
11
+ owned: true;
12
+ ownershipScope?: OwnershipScope;
13
+ scopeKey?: string;
14
+ };
15
+ declare function createGeneratedArtifact(logicalPath: string, content: string, ownership?: ArtifactOwnership): GeneratedArtifact;
16
+ declare function withArtifactOwnership(artifact: GeneratedArtifact, ownership: ArtifactOwnership): GeneratedArtifact;
17
+ declare function mergeGeneratedArtifacts(...groups: GeneratedArtifact[][]): GeneratedArtifact[];
18
+ declare function sharedArtifactOwnership(scopeKey?: string): ArtifactOwnership;
19
+ declare function sliceArtifactOwnership(scopeKey: string): ArtifactOwnership;
20
+ declare function contextArtifactOwnership(scopeKey: string): ArtifactOwnership;
21
+ declare function applyOwnershipIfMissing(artifacts: GeneratedArtifact[], ownership: ArtifactOwnership): GeneratedArtifact[];
22
+ declare function inferArtifactOwnership(logicalPath: string, defaultSliceScopeKey?: string): ArtifactOwnership;
23
+ declare function resolveArtifactOwnership(artifact: Pick<GeneratedArtifact, "logicalPath" | "ownershipScope" | "scopeKey">, defaultSliceScopeKey?: string): ArtifactOwnership;
24
+ //#endregion
25
+ export { contextArtifactOwnership as a, mergeGeneratedArtifacts as c, sliceArtifactOwnership as d, withArtifactOwnership as f, applyOwnershipIfMissing as i, resolveArtifactOwnership as l, GeneratedArtifact as n, createGeneratedArtifact as o, OwnershipScope as r, inferArtifactOwnership as s, ArtifactOwnership as t, sharedArtifactOwnership as u };
@@ -0,0 +1,68 @@
1
+ import { n as GeneratedArtifact } from "./artifacts-QqCpfT50.mjs";
2
+ import { z } from "zod";
3
+
4
+ //#region packages/core/materialize.d.ts
5
+ declare const OwnedManifestEntrySchema: z.ZodObject<{
6
+ logicalPath: z.ZodString;
7
+ ownershipScope: z.ZodEnum<{
8
+ "shared-owned": "shared-owned";
9
+ "slice-owned": "slice-owned";
10
+ "context-owned": "context-owned";
11
+ }>;
12
+ scopeKey: z.ZodString;
13
+ sourceArtifactId: z.ZodString;
14
+ contentSha256: z.ZodString;
15
+ generatorVersion: z.ZodString;
16
+ }, z.core.$strict>;
17
+ declare const MaterializationManifestSchema: z.ZodObject<{
18
+ targetRoot: z.ZodString;
19
+ files: z.ZodArray<z.ZodObject<{
20
+ logicalPath: z.ZodString;
21
+ ownershipScope: z.ZodEnum<{
22
+ "shared-owned": "shared-owned";
23
+ "slice-owned": "slice-owned";
24
+ "context-owned": "context-owned";
25
+ }>;
26
+ scopeKey: z.ZodString;
27
+ sourceArtifactId: z.ZodString;
28
+ contentSha256: z.ZodString;
29
+ generatorVersion: z.ZodString;
30
+ }, z.core.$strict>>;
31
+ }, z.core.$strict>;
32
+ declare const LegacyMaterializationManifestSchema: z.ZodObject<{
33
+ targetRoot: z.ZodString;
34
+ files: z.ZodArray<z.ZodObject<{
35
+ logicalPath: z.ZodString;
36
+ owned: z.ZodLiteral<true>;
37
+ sourceArtifactId: z.ZodString;
38
+ contentSha256: z.ZodString;
39
+ }, z.core.$strict>>;
40
+ }, z.core.$strict>;
41
+ type LegacyMaterializationManifest = z.output<typeof LegacyMaterializationManifestSchema>;
42
+ type OwnedManifestEntry = z.output<typeof OwnedManifestEntrySchema>;
43
+ type MaterializationManifest = z.output<typeof MaterializationManifestSchema>;
44
+ type MaterializationAction = "write" | "overwrite" | "conflict";
45
+ type MaterializedFile = {
46
+ logicalPath: string;
47
+ action: Exclude<MaterializationAction, "conflict"> | "delete";
48
+ };
49
+ type MaterializeResult = {
50
+ targetRoot: string;
51
+ files: MaterializedFile[];
52
+ };
53
+ type MaterializationScope = Pick<OwnedManifestEntry, "ownershipScope" | "scopeKey">;
54
+ type MaterializeOptions = {
55
+ reconcileScopes?: MaterializationScope[];
56
+ };
57
+ type PreparedArtifact = {
58
+ artifact: GeneratedArtifact;
59
+ content: string;
60
+ };
61
+ declare function determineMaterializationAction(destinationExists: boolean, previousManifestSha256?: string, currentDestinationSha256?: string): MaterializationAction;
62
+ declare function sha256Hex(content: string): Promise<string>;
63
+ declare function buildMaterializationManifest(targetRoot: string, artifacts: GeneratedArtifact[] | PreparedArtifact[], generatorVersion: string): Promise<MaterializationManifest>;
64
+ declare function upgradeLegacyMaterializationManifest(legacyManifest: LegacyMaterializationManifest): MaterializationManifest;
65
+ declare function getPersistedMaterializationManifestPath(targetRoot: string): string;
66
+ declare function materializeOwnedArtifacts(targetRoot: string, artifacts: GeneratedArtifact[], generatorVersion: string, options?: MaterializeOptions): Promise<MaterializeResult>;
67
+ //#endregion
68
+ export { LegacyMaterializationManifest, MaterializationAction, MaterializationManifest, MaterializationManifestSchema, MaterializationScope, MaterializeOptions, MaterializeResult, MaterializedFile, OwnedManifestEntry, OwnedManifestEntrySchema, buildMaterializationManifest, determineMaterializationAction, getPersistedMaterializationManifestPath, materializeOwnedArtifacts, sha256Hex, upgradeLegacyMaterializationManifest };
@@ -0,0 +1,240 @@
1
+ import { c as sliceArtifactOwnership, i as inferArtifactOwnership, o as resolveArtifactOwnership } from "./artifacts-CUzHfc15.mjs";
2
+ import { z } from "zod";
3
+ import { dirname, join } from "node:path";
4
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
5
+
6
+ //#region packages/core/materialize.ts
7
+ let _oxfmt;
8
+ async function loadOxfmt() {
9
+ if (!_oxfmt) _oxfmt = await import("oxfmt");
10
+ return _oxfmt;
11
+ }
12
+ const OwnershipScopeSchema = z.enum([
13
+ "shared-owned",
14
+ "slice-owned",
15
+ "context-owned"
16
+ ]);
17
+ const OwnedManifestEntrySchema = z.strictObject({
18
+ logicalPath: z.string().min(1),
19
+ ownershipScope: OwnershipScopeSchema,
20
+ scopeKey: z.string().min(1),
21
+ sourceArtifactId: z.string().min(1),
22
+ contentSha256: z.string().length(64),
23
+ generatorVersion: z.string().min(1)
24
+ });
25
+ const MaterializationManifestSchema = z.strictObject({
26
+ targetRoot: z.string().min(1),
27
+ files: z.array(OwnedManifestEntrySchema)
28
+ });
29
+ const LegacyMaterializationManifestFileSchema = z.strictObject({
30
+ logicalPath: z.string().min(1),
31
+ owned: z.literal(true),
32
+ sourceArtifactId: z.string().min(1),
33
+ contentSha256: z.string().length(64)
34
+ });
35
+ const LegacyMaterializationManifestSchema = z.strictObject({
36
+ targetRoot: z.string().min(1),
37
+ files: z.array(LegacyMaterializationManifestFileSchema)
38
+ });
39
+ const persistedMaterializationManifestRelativePath = ".bounded-context-codegen/materialization-manifest.json";
40
+ function determineMaterializationAction(destinationExists, previousManifestSha256, currentDestinationSha256) {
41
+ if (!destinationExists) return "write";
42
+ if (!previousManifestSha256 || !currentDestinationSha256) return "conflict";
43
+ return currentDestinationSha256 === previousManifestSha256 ? "overwrite" : "conflict";
44
+ }
45
+ async function sha256Hex(content) {
46
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content));
47
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
48
+ }
49
+ async function buildMaterializationManifest(targetRoot, artifacts, generatorVersion) {
50
+ return {
51
+ targetRoot,
52
+ files: await Promise.all(artifacts.map(async (entry) => {
53
+ const artifact = "artifact" in entry ? entry.artifact : entry;
54
+ const content = "content" in entry ? entry.content : await prepareArtifactContent(artifact);
55
+ const ownership = resolveArtifactOwnership(artifact);
56
+ return {
57
+ logicalPath: artifact.logicalPath,
58
+ ownershipScope: ownership.ownershipScope,
59
+ scopeKey: ownership.scopeKey,
60
+ sourceArtifactId: artifact.dataName,
61
+ contentSha256: await sha256Hex(content),
62
+ generatorVersion
63
+ };
64
+ }))
65
+ };
66
+ }
67
+ function inferLegacyManifestSliceScopeKey(legacyManifest) {
68
+ const unresolvedScopeKey = "__legacy-unresolved__";
69
+ const inferredScopeKeys = new Set(legacyManifest.files.flatMap((file) => {
70
+ if (!file.logicalPath.startsWith("core/contexts/") && !file.logicalPath.includes("/repositories/")) return [];
71
+ const ownership = inferArtifactOwnership(file.logicalPath, unresolvedScopeKey);
72
+ return ownership.ownershipScope === "slice-owned" && ownership.scopeKey !== unresolvedScopeKey ? [ownership.scopeKey] : [];
73
+ }));
74
+ return inferredScopeKeys.size === 1 ? [...inferredScopeKeys][0] : void 0;
75
+ }
76
+ function upgradeLegacyMaterializationManifest(legacyManifest) {
77
+ const defaultSliceScopeKey = inferLegacyManifestSliceScopeKey(legacyManifest) ?? "default";
78
+ return {
79
+ targetRoot: legacyManifest.targetRoot,
80
+ files: legacyManifest.files.map((file) => {
81
+ const ownership = file.logicalPath.startsWith("presentation/") || file.logicalPath.startsWith("routes/") ? sliceArtifactOwnership(defaultSliceScopeKey) : resolveArtifactOwnership(file, defaultSliceScopeKey);
82
+ return {
83
+ logicalPath: file.logicalPath,
84
+ ownershipScope: ownership.ownershipScope,
85
+ scopeKey: ownership.scopeKey,
86
+ sourceArtifactId: file.sourceArtifactId,
87
+ contentSha256: file.contentSha256,
88
+ generatorVersion: "legacy-v1"
89
+ };
90
+ })
91
+ };
92
+ }
93
+ function getPersistedMaterializationManifestPath(targetRoot) {
94
+ return join(targetRoot, persistedMaterializationManifestRelativePath);
95
+ }
96
+ function ownershipScopeKey(entry) {
97
+ return `${entry.ownershipScope}:${entry.scopeKey}`;
98
+ }
99
+ function scopedLogicalPathKey(entry) {
100
+ return `${ownershipScopeKey(entry)}:${entry.logicalPath}`;
101
+ }
102
+ function findScopedManifestEntry(entries, currentEntry) {
103
+ const currentOwnershipScopeKey = ownershipScopeKey(currentEntry);
104
+ return entries.find((entry) => ownershipScopeKey(entry) === currentOwnershipScopeKey && (entry.sourceArtifactId === currentEntry.sourceArtifactId || entry.logicalPath === currentEntry.logicalPath));
105
+ }
106
+ async function readDestinationSha256(destinationPath) {
107
+ try {
108
+ return await sha256Hex(await readFile(destinationPath, "utf-8"));
109
+ } catch (error) {
110
+ if (error.code === "ENOENT") return;
111
+ throw error;
112
+ }
113
+ }
114
+ async function readPersistedMaterializationManifest(targetRoot) {
115
+ try {
116
+ const persistedManifestText = await readFile(getPersistedMaterializationManifestPath(targetRoot), "utf-8");
117
+ const persistedManifest = JSON.parse(persistedManifestText);
118
+ const v2Manifest = MaterializationManifestSchema.safeParse(persistedManifest);
119
+ if (v2Manifest.success) return {
120
+ kind: "v2",
121
+ manifest: v2Manifest.data
122
+ };
123
+ const legacyManifest = LegacyMaterializationManifestSchema.safeParse(persistedManifest);
124
+ if (legacyManifest.success) return {
125
+ kind: "legacy-v1",
126
+ manifest: legacyManifest.data,
127
+ upgradedManifest: upgradeLegacyMaterializationManifest(legacyManifest.data)
128
+ };
129
+ return {
130
+ kind: "v2",
131
+ manifest: MaterializationManifestSchema.parse(persistedManifest)
132
+ };
133
+ } catch (error) {
134
+ if (error.code === "ENOENT") return { kind: "missing" };
135
+ throw error;
136
+ }
137
+ }
138
+ function indexLegacyManifestEntriesByLogicalPath(manifest) {
139
+ const entries = /* @__PURE__ */ new Map();
140
+ for (const file of manifest.files) {
141
+ if (entries.has(file.logicalPath)) throw new Error(`Legacy materialization manifest is ambiguous for logical path: ${file.logicalPath}`);
142
+ entries.set(file.logicalPath, file);
143
+ }
144
+ return entries;
145
+ }
146
+ async function removePathIfExists(path) {
147
+ try {
148
+ await rm(path);
149
+ } catch (error) {
150
+ if (!(error.code === "ENOENT")) throw error;
151
+ }
152
+ }
153
+ function shouldFormatArtifact(logicalPath) {
154
+ return /\.(?:[cm]?tsx?|jsx?)$/.test(logicalPath);
155
+ }
156
+ async function prepareArtifactContent(artifact) {
157
+ if (!shouldFormatArtifact(artifact.logicalPath)) return artifact.content;
158
+ let currentContent = artifact.content;
159
+ for (let pass = 0; pass < 3; pass++) {
160
+ const result = await (await loadOxfmt()).format(artifact.logicalPath, currentContent, { singleQuote: true });
161
+ if (result.errors.length > 0) throw new Error(`Failed to format generated artifact ${artifact.logicalPath}: ${result.errors.join(", ")}`);
162
+ if (result.code === currentContent) return result.code;
163
+ currentContent = result.code;
164
+ }
165
+ return currentContent;
166
+ }
167
+ async function prepareArtifactsForMaterialization(artifacts) {
168
+ return await Promise.all(artifacts.map(async (artifact) => ({
169
+ artifact,
170
+ content: await prepareArtifactContent(artifact)
171
+ })));
172
+ }
173
+ async function materializeOwnedArtifacts(targetRoot, artifacts, generatorVersion, options = {}) {
174
+ const preparedArtifacts = await prepareArtifactsForMaterialization(artifacts);
175
+ const manifest = await buildMaterializationManifest(targetRoot, preparedArtifacts, generatorVersion);
176
+ const previousManifest = await readPersistedMaterializationManifest(targetRoot);
177
+ const previousOwnedEntries = previousManifest.kind === "missing" ? [] : previousManifest.kind === "legacy-v1" ? (() => {
178
+ indexLegacyManifestEntriesByLogicalPath(previousManifest.manifest);
179
+ return previousManifest.upgradedManifest.files;
180
+ })() : previousManifest.manifest.files;
181
+ const activeOwnershipScopes = new Set([...manifest.files, ...options.reconcileScopes ?? []].map((file) => ownershipScopeKey(file)));
182
+ const currentManifestEntriesBySourceArtifactId = new Map(manifest.files.map((file) => [file.sourceArtifactId, file]));
183
+ const currentManifestEntriesByScopedLogicalPath = new Set(manifest.files.map((file) => scopedLogicalPathKey(file)));
184
+ const plannedWrites = [];
185
+ const plannedDeletes = [];
186
+ for (const preparedArtifact of preparedArtifacts) {
187
+ const { artifact, content } = preparedArtifact;
188
+ const destinationPath = join(targetRoot, artifact.logicalPath);
189
+ const destinationSha256 = await readDestinationSha256(destinationPath);
190
+ const currentManifestEntry = currentManifestEntriesBySourceArtifactId.get(artifact.dataName);
191
+ if (currentManifestEntry === void 0) throw new Error(`Materialization manifest missing current entry for artifact: ${artifact.logicalPath}`);
192
+ const previousManifestEntry = findScopedManifestEntry(previousOwnedEntries, currentManifestEntry);
193
+ const action = determineMaterializationAction(destinationSha256 !== void 0, previousManifestEntry?.contentSha256, destinationSha256);
194
+ if (action === "conflict") throw new Error(`Materialization conflict for generator-owned file: ${artifact.logicalPath}`);
195
+ plannedWrites.push({
196
+ artifact: {
197
+ ...artifact,
198
+ content
199
+ },
200
+ destinationPath,
201
+ action
202
+ });
203
+ }
204
+ for (const previousManifestEntry of previousOwnedEntries) {
205
+ if (!activeOwnershipScopes.has(ownershipScopeKey(previousManifestEntry))) continue;
206
+ if (currentManifestEntriesByScopedLogicalPath.has(scopedLogicalPathKey(previousManifestEntry))) continue;
207
+ const destinationPath = join(targetRoot, previousManifestEntry.logicalPath);
208
+ const destinationSha256 = await readDestinationSha256(destinationPath);
209
+ if (destinationSha256 !== void 0 && destinationSha256 !== previousManifestEntry.contentSha256) throw new Error(`Materialization conflict for stale generator-owned file: ${previousManifestEntry.logicalPath}`);
210
+ plannedDeletes.push({
211
+ logicalPath: previousManifestEntry.logicalPath,
212
+ destinationPath
213
+ });
214
+ }
215
+ for (const staleFile of plannedDeletes) await removePathIfExists(staleFile.destinationPath);
216
+ for (const write of plannedWrites) {
217
+ await mkdir(dirname(write.destinationPath), { recursive: true });
218
+ await writeFile(write.destinationPath, write.artifact.content, "utf-8");
219
+ }
220
+ const persistedManifestPath = getPersistedMaterializationManifestPath(targetRoot);
221
+ const persistedManifest = {
222
+ targetRoot,
223
+ files: [...previousOwnedEntries.filter((file) => !activeOwnershipScopes.has(ownershipScopeKey(file))), ...manifest.files]
224
+ };
225
+ await mkdir(dirname(persistedManifestPath), { recursive: true });
226
+ await writeFile(persistedManifestPath, `${JSON.stringify(persistedManifest, null, 2)}\n`, "utf-8");
227
+ return {
228
+ targetRoot,
229
+ files: [...plannedDeletes.map((staleFile) => ({
230
+ logicalPath: staleFile.logicalPath,
231
+ action: "delete"
232
+ })), ...plannedWrites.map((write) => ({
233
+ logicalPath: write.artifact.logicalPath,
234
+ action: write.action
235
+ }))]
236
+ };
237
+ }
238
+
239
+ //#endregion
240
+ export { MaterializationManifestSchema, OwnedManifestEntrySchema, buildMaterializationManifest, determineMaterializationAction, getPersistedMaterializationManifestPath, materializeOwnedArtifacts, sha256Hex, upgradeLegacyMaterializationManifest };