@stardeck-customer-apps/compose 0.8.0 → 0.10.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/SKILL.md +25 -0
- package/dist/cli.js +475 -353
- package/dist/index.d.mts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +627 -513
- package/dist/index.mjs +623 -513
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2066,23 +2066,23 @@ function isValidModuleName(value) {
|
|
|
2066
2066
|
var projectSlugSchema = import_zod.z.string().refine((value) => validateSlug(value).valid, "must be a valid project slug");
|
|
2067
2067
|
var moduleKindSchema = import_zod.z.enum(["feature", "library", "contract"]);
|
|
2068
2068
|
var endpointMethodSchema = import_zod.z.enum(ENDPOINT_METHODS);
|
|
2069
|
-
function endpointPathValidationError(
|
|
2070
|
-
if (!
|
|
2069
|
+
function endpointPathValidationError(path5) {
|
|
2070
|
+
if (!path5.startsWith("/api/")) {
|
|
2071
2071
|
return 'must begin with "/api/"';
|
|
2072
2072
|
}
|
|
2073
|
-
if (
|
|
2073
|
+
if (path5.endsWith("/")) {
|
|
2074
2074
|
return "must not end with a trailing slash";
|
|
2075
2075
|
}
|
|
2076
|
-
if (
|
|
2076
|
+
if (path5.includes("?") || path5.includes("#")) {
|
|
2077
2077
|
return "must not include query or hash";
|
|
2078
2078
|
}
|
|
2079
|
-
if (
|
|
2079
|
+
if (path5.includes("\\")) {
|
|
2080
2080
|
return "must not include backslashes";
|
|
2081
2081
|
}
|
|
2082
|
-
if (
|
|
2082
|
+
if (path5.includes("%")) {
|
|
2083
2083
|
return "must not include percent-encoded segments";
|
|
2084
2084
|
}
|
|
2085
|
-
const parts =
|
|
2085
|
+
const parts = path5.split("/");
|
|
2086
2086
|
if (parts.length < 3 || parts[0] !== "" || parts[1] !== "api") {
|
|
2087
2087
|
return 'must begin with "/api/"';
|
|
2088
2088
|
}
|
|
@@ -2132,12 +2132,12 @@ function endpointPathValidationError(path3) {
|
|
|
2132
2132
|
}
|
|
2133
2133
|
return null;
|
|
2134
2134
|
}
|
|
2135
|
-
function normalizeEndpointPathForCollision(
|
|
2136
|
-
const error = endpointPathValidationError(
|
|
2135
|
+
function normalizeEndpointPathForCollision(path5) {
|
|
2136
|
+
const error = endpointPathValidationError(path5);
|
|
2137
2137
|
if (error != null) {
|
|
2138
2138
|
throw new Error(`invalid endpoint path: ${error}`);
|
|
2139
2139
|
}
|
|
2140
|
-
return
|
|
2140
|
+
return path5.split("/").map((segment, index) => {
|
|
2141
2141
|
if (index < 2) {
|
|
2142
2142
|
return segment;
|
|
2143
2143
|
}
|
|
@@ -2150,8 +2150,8 @@ function normalizeEndpointPathForCollision(path3) {
|
|
|
2150
2150
|
return segment;
|
|
2151
2151
|
}).join("/");
|
|
2152
2152
|
}
|
|
2153
|
-
var endpointPathSchema = import_zod.z.string().superRefine((
|
|
2154
|
-
const error = endpointPathValidationError(
|
|
2153
|
+
var endpointPathSchema = import_zod.z.string().superRefine((path5, ctx) => {
|
|
2154
|
+
const error = endpointPathValidationError(path5);
|
|
2155
2155
|
if (error != null) {
|
|
2156
2156
|
ctx.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: error });
|
|
2157
2157
|
}
|
|
@@ -2398,16 +2398,16 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2398
2398
|
}
|
|
2399
2399
|
}
|
|
2400
2400
|
const seenWorkflows = /* @__PURE__ */ new Map();
|
|
2401
|
-
const noteWorkflow = (name,
|
|
2401
|
+
const noteWorkflow = (name, path5) => {
|
|
2402
2402
|
const existing = seenWorkflows.get(name);
|
|
2403
2403
|
if (existing != null) {
|
|
2404
2404
|
ctx.addIssue({
|
|
2405
2405
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2406
2406
|
message: `duplicate workflow name "${name}" (also at ${existing})`,
|
|
2407
|
-
path:
|
|
2407
|
+
path: path5
|
|
2408
2408
|
});
|
|
2409
2409
|
} else {
|
|
2410
|
-
seenWorkflows.set(name,
|
|
2410
|
+
seenWorkflows.set(name, path5.join("."));
|
|
2411
2411
|
}
|
|
2412
2412
|
};
|
|
2413
2413
|
for (let i = 0; i < manifest.workflows.length; i += 1) {
|
|
@@ -2419,7 +2419,7 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2419
2419
|
}
|
|
2420
2420
|
}
|
|
2421
2421
|
const seenEndpointKeys = /* @__PURE__ */ new Map();
|
|
2422
|
-
const noteEndpoint = (claim,
|
|
2422
|
+
const noteEndpoint = (claim, path5) => {
|
|
2423
2423
|
let key;
|
|
2424
2424
|
try {
|
|
2425
2425
|
key = normalizeEndpointPathForCollision(claim.path);
|
|
@@ -2431,10 +2431,10 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2431
2431
|
ctx.addIssue({
|
|
2432
2432
|
code: import_zod.z.ZodIssueCode.custom,
|
|
2433
2433
|
message: `duplicate normalized endpoint path "${key}" (also at ${existing})`,
|
|
2434
|
-
path:
|
|
2434
|
+
path: path5
|
|
2435
2435
|
});
|
|
2436
2436
|
} else {
|
|
2437
|
-
seenEndpointKeys.set(key,
|
|
2437
|
+
seenEndpointKeys.set(key, path5.join("."));
|
|
2438
2438
|
}
|
|
2439
2439
|
};
|
|
2440
2440
|
for (let i = 0; i < manifest.endpoints.length; i += 1) {
|
|
@@ -2535,8 +2535,8 @@ var moduleManifestSchema = import_zod.z.object({
|
|
|
2535
2535
|
}).transform(({ version: _legacyVersion, ...manifest }) => manifest);
|
|
2536
2536
|
function formatZodErrors(error) {
|
|
2537
2537
|
return error.issues.map((issue) => {
|
|
2538
|
-
const
|
|
2539
|
-
return `${
|
|
2538
|
+
const path5 = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
2539
|
+
return `${path5}: ${issue.message}`;
|
|
2540
2540
|
});
|
|
2541
2541
|
}
|
|
2542
2542
|
function parseModuleManifest(raw) {
|
|
@@ -2936,16 +2936,16 @@ var collectedE2eFactsSchema = import_zod3.z.object({
|
|
|
2936
2936
|
function validateRouteExportEvidenceCongruence(input) {
|
|
2937
2937
|
const entrySet = new Set(input.routeEntries);
|
|
2938
2938
|
const exportPaths = input.routePageExports.map((e) => e.nextRelativePath);
|
|
2939
|
-
for (const
|
|
2940
|
-
if (!entrySet.has(
|
|
2941
|
-
input.onIssue(`routePageExports path "${
|
|
2939
|
+
for (const path5 of exportPaths) {
|
|
2940
|
+
if (!entrySet.has(path5)) {
|
|
2941
|
+
input.onIssue(`routePageExports path "${path5}" is not listed in routeEntries`);
|
|
2942
2942
|
}
|
|
2943
2943
|
}
|
|
2944
2944
|
if (input.routeCollectionErrors.length === 0) {
|
|
2945
2945
|
const exportSet = new Set(exportPaths);
|
|
2946
|
-
for (const
|
|
2947
|
-
if (!exportSet.has(
|
|
2948
|
-
input.onIssue(`routeEntries path "${
|
|
2946
|
+
for (const path5 of input.routeEntries) {
|
|
2947
|
+
if (!exportSet.has(path5)) {
|
|
2948
|
+
input.onIssue(`routeEntries path "${path5}" is missing from routePageExports`);
|
|
2949
2949
|
}
|
|
2950
2950
|
}
|
|
2951
2951
|
}
|
|
@@ -3901,12 +3901,13 @@ var MODULES_GEN_PATH = `${APP_WEB_PREFIX}src/modules.gen.ts`;
|
|
|
3901
3901
|
var APP_EXTENSIONS_PATH = `${APP_WEB_PREFIX}src/lib/modules/app-extensions.ts`;
|
|
3902
3902
|
|
|
3903
3903
|
// src/cli.ts
|
|
3904
|
-
var
|
|
3904
|
+
var import_typescript7 = __toESM(require("typescript"));
|
|
3905
|
+
var import_node_path4 = __toESM(require("path"));
|
|
3905
3906
|
|
|
3906
3907
|
// src/index.ts
|
|
3907
3908
|
var import_node_child_process2 = require("child_process");
|
|
3908
|
-
var
|
|
3909
|
-
var
|
|
3909
|
+
var import_node_fs3 = __toESM(require("fs"));
|
|
3910
|
+
var import_node_path3 = __toESM(require("path"));
|
|
3910
3911
|
|
|
3911
3912
|
// ../../packages/lib/src/server/module-rail/local-fs-sandbox.ts
|
|
3912
3913
|
var import_node_child_process = require("child_process");
|
|
@@ -3965,147 +3966,29 @@ function shellQuote(value) {
|
|
|
3965
3966
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
3966
3967
|
}
|
|
3967
3968
|
|
|
3968
|
-
// ../../packages/lib/src/shared/module-composition.ts
|
|
3969
|
-
var import_zod6 = require("zod");
|
|
3970
|
-
var moduleRefSchema = import_zod6.z.object({
|
|
3971
|
-
homeProjectId: import_zod6.z.string().uuid(),
|
|
3972
|
-
moduleName: moduleNameSchema
|
|
3973
|
-
});
|
|
3974
|
-
var creationSourceBlankSchema = import_zod6.z.object({
|
|
3975
|
-
type: import_zod6.z.literal("blank")
|
|
3976
|
-
});
|
|
3977
|
-
var creationSourceBlueprintSchema = import_zod6.z.object({
|
|
3978
|
-
type: import_zod6.z.literal("blueprint"),
|
|
3979
|
-
/** Internal template/fork project id — keep template naming in identifiers. */
|
|
3980
|
-
blueprintProjectId: import_zod6.z.string().uuid(),
|
|
3981
|
-
/**
|
|
3982
|
-
* `null`/absent means every feature Module. `[]` is refused: the platform
|
|
3983
|
-
* serialises it to an empty `NEXT_PUBLIC_STARDECK_ENABLED_MODULES`, which the
|
|
3984
|
-
* enablement parser reads back as "all" (`module-enablement.ts`) — the
|
|
3985
|
-
* opposite of what an empty selection means.
|
|
3986
|
-
*/
|
|
3987
|
-
enabledModules: import_zod6.z.array(moduleNameSchema).min(1, "Enable at least one Module").max(100).nullable().optional()
|
|
3988
|
-
});
|
|
3989
|
-
var creationSourceModulesSchema = import_zod6.z.object({
|
|
3990
|
-
type: import_zod6.z.literal("modules"),
|
|
3991
|
-
requested: import_zod6.z.array(moduleRefSchema).min(1)
|
|
3992
|
-
});
|
|
3993
|
-
var creationSourceSchema = import_zod6.z.discriminatedUnion("type", [
|
|
3994
|
-
creationSourceBlankSchema,
|
|
3995
|
-
creationSourceBlueprintSchema,
|
|
3996
|
-
creationSourceModulesSchema
|
|
3997
|
-
]);
|
|
3998
|
-
var moduleCompositionStatusSchema = import_zod6.z.enum([
|
|
3999
|
-
"pending",
|
|
4000
|
-
"installing",
|
|
4001
|
-
"ready",
|
|
4002
|
-
"partial",
|
|
4003
|
-
"failed"
|
|
4004
|
-
]);
|
|
4005
|
-
var resolvedModuleSnapshotSchema = moduleRefSchema.extend({
|
|
4006
|
-
version: import_zod6.z.string().min(1),
|
|
4007
|
-
homeCommitSha: import_zod6.z.string().min(1)
|
|
4008
|
-
});
|
|
4009
|
-
var moduleInstallHandoffSchema = import_zod6.z.object({
|
|
4010
|
-
moduleName: import_zod6.z.string().min(1),
|
|
4011
|
-
homeProjectId: import_zod6.z.string().uuid(),
|
|
4012
|
-
installedVersion: import_zod6.z.string().min(1),
|
|
4013
|
-
baselineSha: import_zod6.z.string().min(1),
|
|
4014
|
-
mounts: import_zod6.z.array(
|
|
4015
|
-
import_zod6.z.object({
|
|
4016
|
-
group: import_zod6.z.string(),
|
|
4017
|
-
segment: import_zod6.z.string()
|
|
4018
|
-
})
|
|
4019
|
-
),
|
|
4020
|
-
integration: import_zod6.z.array(import_zod6.z.string()),
|
|
4021
|
-
workflows: import_zod6.z.array(import_zod6.z.string()),
|
|
4022
|
-
prunedEnhancements: import_zod6.z.array(import_zod6.z.string()),
|
|
4023
|
-
restoredEnhancements: import_zod6.z.array(import_zod6.z.string()).optional(),
|
|
4024
|
-
permissions: import_zod6.z.array(
|
|
4025
|
-
import_zod6.z.object({
|
|
4026
|
-
key: import_zod6.z.string().min(1),
|
|
4027
|
-
description: import_zod6.z.string().min(1)
|
|
4028
|
-
})
|
|
4029
|
-
).optional()
|
|
4030
|
-
});
|
|
4031
|
-
var moduleCompositionRunAttemptSchema = import_zod6.z.object({
|
|
4032
|
-
module: moduleRefSchema,
|
|
4033
|
-
runId: import_zod6.z.string().min(1),
|
|
4034
|
-
status: import_zod6.z.string().min(1),
|
|
4035
|
-
schemaApplied: import_zod6.z.boolean(),
|
|
4036
|
-
error: import_zod6.z.string().optional(),
|
|
4037
|
-
/** Prefer full ModuleInstallHandoff; identity fields optional for older rows. */
|
|
4038
|
-
handoff: moduleInstallHandoffSchema.partial({ moduleName: true, homeProjectId: true }).optional()
|
|
4039
|
-
});
|
|
4040
|
-
var initialModuleCompositionSchema = import_zod6.z.object({
|
|
4041
|
-
version: import_zod6.z.literal(1),
|
|
4042
|
-
status: moduleCompositionStatusSchema,
|
|
4043
|
-
requested: import_zod6.z.array(moduleRefSchema).min(1),
|
|
4044
|
-
resolved: import_zod6.z.array(resolvedModuleSnapshotSchema),
|
|
4045
|
-
installOrder: import_zod6.z.array(moduleRefSchema),
|
|
4046
|
-
runs: import_zod6.z.array(moduleCompositionRunAttemptSchema).default([]),
|
|
4047
|
-
/**
|
|
4048
|
-
* True when any failed/partial attempt applied schema before failing.
|
|
4049
|
-
* Never claim transactional rollback across git/schema.
|
|
4050
|
-
*/
|
|
4051
|
-
schemaApplied: import_zod6.z.boolean().default(false),
|
|
4052
|
-
/**
|
|
4053
|
-
* Present when retry would silently change pinned home SHAs — caller must
|
|
4054
|
-
* reconfirm the new plan rather than continue.
|
|
4055
|
-
*/
|
|
4056
|
-
reconfirmationRequired: import_zod6.z.object({
|
|
4057
|
-
reason: import_zod6.z.string(),
|
|
4058
|
-
previous: import_zod6.z.array(resolvedModuleSnapshotSchema),
|
|
4059
|
-
next: import_zod6.z.array(resolvedModuleSnapshotSchema)
|
|
4060
|
-
}).optional(),
|
|
4061
|
-
error: import_zod6.z.string().optional(),
|
|
4062
|
-
/**
|
|
4063
|
-
* First-turn system handoff lifecycle. Absent/`pending` means inject when
|
|
4064
|
-
* status is ready; `consumed` means a successful agent turn already used it.
|
|
4065
|
-
*/
|
|
4066
|
-
systemHandoffStatus: import_zod6.z.enum(["pending", "consumed"]).optional(),
|
|
4067
|
-
/**
|
|
4068
|
-
* CAS ownership token for concurrent retry attempts. Only the attempt that
|
|
4069
|
-
* currently owns this value may reset or compensate durable composition state.
|
|
4070
|
-
*/
|
|
4071
|
-
retryAttemptId: import_zod6.z.string().min(1).optional(),
|
|
4072
|
-
/**
|
|
4073
|
-
* When the current `installing` attempt was claimed. A setup run that dies
|
|
4074
|
-
* with its host (a killed Vercel instance) leaves the composition `installing`
|
|
4075
|
-
* forever, and the creating page's poll only stops on ready/partial/failed —
|
|
4076
|
-
* so staleness, not a queue's stall detector, is what settles it. Absent on
|
|
4077
|
-
* rows claimed before this field existed; treated as stale.
|
|
4078
|
-
*/
|
|
4079
|
-
attemptStartedAt: import_zod6.z.string().datetime().optional(),
|
|
4080
|
-
/** Outside-module files the agent changed during connection work. */
|
|
4081
|
-
integrationFiles: import_zod6.z.array(import_zod6.z.string()).optional(),
|
|
4082
|
-
verificationSummary: import_zod6.z.string().optional()
|
|
4083
|
-
});
|
|
4084
|
-
var MODULE_SETUP_ATTEMPT_STALE_MS = 10 * 60 * 1e3;
|
|
4085
|
-
|
|
4086
3969
|
// ../../packages/lib/src/shared/compose-inputs.ts
|
|
4087
|
-
var
|
|
3970
|
+
var import_zod6 = require("zod");
|
|
4088
3971
|
var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
4089
|
-
var composeInputsSchema =
|
|
4090
|
-
connectedStores:
|
|
4091
|
-
|
|
4092
|
-
storeId:
|
|
4093
|
-
slug:
|
|
3972
|
+
var composeInputsSchema = import_zod6.z.strictObject({
|
|
3973
|
+
connectedStores: import_zod6.z.array(
|
|
3974
|
+
import_zod6.z.strictObject({
|
|
3975
|
+
storeId: import_zod6.z.string().min(1),
|
|
3976
|
+
slug: import_zod6.z.string().min(1),
|
|
4094
3977
|
// A row with an empty binding key must not fail a deploy: the rail
|
|
4095
3978
|
// treats falsy as "no binding key", so normalise rather than reject.
|
|
4096
|
-
bindingKey:
|
|
4097
|
-
accessLevel:
|
|
3979
|
+
bindingKey: import_zod6.z.string().nullish().transform((value) => value || null),
|
|
3980
|
+
accessLevel: import_zod6.z.enum(["read", "write", "admin"]),
|
|
4098
3981
|
// Defaulted so a file written before the platform emitted store types
|
|
4099
3982
|
// (database rows only) still parses. The platform starts writing
|
|
4100
3983
|
// storage rows, with `type` set, once forks run a compose that reads it.
|
|
4101
|
-
type:
|
|
3984
|
+
type: import_zod6.z.enum(["database", "storage"]).default("database")
|
|
4102
3985
|
})
|
|
4103
3986
|
).default([]),
|
|
4104
|
-
installs:
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
installedVersion:
|
|
4108
|
-
datastoreBindings:
|
|
3987
|
+
installs: import_zod6.z.record(
|
|
3988
|
+
import_zod6.z.string(),
|
|
3989
|
+
import_zod6.z.strictObject({
|
|
3990
|
+
installedVersion: import_zod6.z.string().min(1),
|
|
3991
|
+
datastoreBindings: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.string()).nullish()
|
|
4109
3992
|
})
|
|
4110
3993
|
).default({})
|
|
4111
3994
|
});
|
|
@@ -4262,13 +4145,13 @@ function assertClientSafeConventionalEntryImports(source, pathLabel, kind) {
|
|
|
4262
4145
|
function createMessageTree() {
|
|
4263
4146
|
return /* @__PURE__ */ Object.create(null);
|
|
4264
4147
|
}
|
|
4265
|
-
function assertMessageTree(value,
|
|
4148
|
+
function assertMessageTree(value, path5) {
|
|
4266
4149
|
if (typeof value === "string") return;
|
|
4267
4150
|
if (value === null || Array.isArray(value) || typeof value !== "object") {
|
|
4268
|
-
throw new Error(`Invalid message at ${
|
|
4151
|
+
throw new Error(`Invalid message at ${path5}: expected string or nested object`);
|
|
4269
4152
|
}
|
|
4270
4153
|
for (const [key, child] of Object.entries(value)) {
|
|
4271
|
-
assertMessageTree(child, `${
|
|
4154
|
+
assertMessageTree(child, `${path5}.${key}`);
|
|
4272
4155
|
}
|
|
4273
4156
|
}
|
|
4274
4157
|
function cloneTree(value) {
|
|
@@ -4278,17 +4161,17 @@ function cloneTree(value) {
|
|
|
4278
4161
|
}
|
|
4279
4162
|
return clone;
|
|
4280
4163
|
}
|
|
4281
|
-
function collectLeafPaths(value,
|
|
4164
|
+
function collectLeafPaths(value, path5, out) {
|
|
4282
4165
|
if (typeof value === "string") {
|
|
4283
|
-
out.push(
|
|
4166
|
+
out.push(path5);
|
|
4284
4167
|
return;
|
|
4285
4168
|
}
|
|
4286
|
-
assertMessageTree(value,
|
|
4287
|
-
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${
|
|
4169
|
+
assertMessageTree(value, path5);
|
|
4170
|
+
for (const [key, child] of Object.entries(value)) collectLeafPaths(child, `${path5}.${key}`, out);
|
|
4288
4171
|
}
|
|
4289
|
-
function overrideInto(target, source, owner,
|
|
4172
|
+
function overrideInto(target, source, owner, path5, missing) {
|
|
4290
4173
|
for (const [key, value] of Object.entries(source)) {
|
|
4291
|
-
const nextPath =
|
|
4174
|
+
const nextPath = path5 ? `${path5}.${key}` : key;
|
|
4292
4175
|
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
|
4293
4176
|
collectLeafPaths(value, nextPath, missing);
|
|
4294
4177
|
continue;
|
|
@@ -4315,9 +4198,9 @@ function overrideInto(target, source, owner, path3, missing) {
|
|
|
4315
4198
|
function composeLocaleMessagesForPlan(layers) {
|
|
4316
4199
|
const result = createMessageTree();
|
|
4317
4200
|
const leafOwners = /* @__PURE__ */ new Map();
|
|
4318
|
-
function mergeInto(target, source, owner,
|
|
4201
|
+
function mergeInto(target, source, owner, path5) {
|
|
4319
4202
|
for (const [key, value] of Object.entries(source)) {
|
|
4320
|
-
const nextPath =
|
|
4203
|
+
const nextPath = path5 ? `${path5}.${key}` : key;
|
|
4321
4204
|
const hasExisting = Object.prototype.hasOwnProperty.call(target, key);
|
|
4322
4205
|
const existing = hasExisting ? target[key] : void 0;
|
|
4323
4206
|
if (typeof value === "string") {
|
|
@@ -4861,8 +4744,8 @@ var METHOD_ORDER = {
|
|
|
4861
4744
|
function compareNames2(a, b) {
|
|
4862
4745
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
4863
4746
|
}
|
|
4864
|
-
function endpointPairKey(method,
|
|
4865
|
-
return `${method} ${
|
|
4747
|
+
function endpointPairKey(method, path5) {
|
|
4748
|
+
return `${method} ${path5}`;
|
|
4866
4749
|
}
|
|
4867
4750
|
function apiPathToRouteFile(apiPath) {
|
|
4868
4751
|
if (!apiPath.startsWith("/api/")) {
|
|
@@ -4876,11 +4759,11 @@ function routeEntryToAppPageFile(nextRelativePath) {
|
|
|
4876
4759
|
}
|
|
4877
4760
|
var APP_ROUTE_FILE_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
4878
4761
|
var API_ROUTE_FILE_SUFFIXES = ["/route.ts", "/route.tsx", "/route.jsx", "/route.js"];
|
|
4879
|
-
function appRouteFileToNextRelativePath(
|
|
4880
|
-
const pagePath = appPageFileToNextRelativePath(
|
|
4762
|
+
function appRouteFileToNextRelativePath(path5) {
|
|
4763
|
+
const pagePath = appPageFileToNextRelativePath(path5, APP_PACKAGE_DIR);
|
|
4881
4764
|
if (pagePath != null) return pagePath;
|
|
4882
|
-
if (!
|
|
4883
|
-
const relative =
|
|
4765
|
+
if (!path5.startsWith(APP_ROUTE_FILE_PREFIX)) return null;
|
|
4766
|
+
const relative = path5.slice(APP_ROUTE_FILE_PREFIX.length);
|
|
4884
4767
|
if (!relative.startsWith("api/")) return null;
|
|
4885
4768
|
const suffix = API_ROUTE_FILE_SUFFIXES.find((candidate) => relative.endsWith(candidate));
|
|
4886
4769
|
if (!suffix) return null;
|
|
@@ -4972,9 +4855,9 @@ function legacyHandlerPropertyMatchesPath(property, method, physicalPath, bindin
|
|
|
4972
4855
|
const expectedTokens = knownAliases[`${binding} ${method} ${physicalPath}`] ?? pathTokens;
|
|
4973
4856
|
return handlerTokens.length === expectedTokens.length && handlerTokens.every((token, index) => token === expectedTokens[index]);
|
|
4974
4857
|
}
|
|
4975
|
-
function parseLegacyApiAdapter(
|
|
4976
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
4977
|
-
if (expectedMarkerKindForPath(
|
|
4858
|
+
function parseLegacyApiAdapter(path5, source) {
|
|
4859
|
+
const physicalPath = appRouteFileToNextRelativePath(path5);
|
|
4860
|
+
if (expectedMarkerKindForPath(path5) !== "endpoint" || physicalPath == null) return null;
|
|
4978
4861
|
const firstLine = source.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
4979
4862
|
const match = firstLine.match(
|
|
4980
4863
|
new RegExp(
|
|
@@ -4984,7 +4867,7 @@ function parseLegacyApiAdapter(path3, source) {
|
|
|
4984
4867
|
if (!match || match[2] !== physicalPath) return null;
|
|
4985
4868
|
const owner = match[1];
|
|
4986
4869
|
if (!isValidModuleName(owner)) return null;
|
|
4987
|
-
const file = import_typescript4.default.createSourceFile(
|
|
4870
|
+
const file = import_typescript4.default.createSourceFile(path5, source, import_typescript4.default.ScriptTarget.Latest, false, import_typescript4.default.ScriptKind.TS);
|
|
4988
4871
|
if (file.parseDiagnostics.length > 0) {
|
|
4989
4872
|
return null;
|
|
4990
4873
|
}
|
|
@@ -5026,9 +4909,9 @@ function parseLegacyApiAdapter(path3, source) {
|
|
|
5026
4909
|
}
|
|
5027
4910
|
return handlerBinding != null && methods.size > 0 ? { owner, methods: [...methods] } : null;
|
|
5028
4911
|
}
|
|
5029
|
-
function parseLegacyRouteAdapter(
|
|
5030
|
-
const physicalPath = appRouteFileToNextRelativePath(
|
|
5031
|
-
if (expectedMarkerKindForPath(
|
|
4912
|
+
function parseLegacyRouteAdapter(path5, source) {
|
|
4913
|
+
const physicalPath = appRouteFileToNextRelativePath(path5);
|
|
4914
|
+
if (expectedMarkerKindForPath(path5) !== "route" || physicalPath == null) return null;
|
|
5032
4915
|
const normalized = source.replace(/\r\n/g, "\n");
|
|
5033
4916
|
const match = normalized.match(
|
|
5034
4917
|
new RegExp(
|
|
@@ -5040,30 +4923,30 @@ function parseLegacyRouteAdapter(path3, source) {
|
|
|
5040
4923
|
const nextRelativePath = match[2];
|
|
5041
4924
|
return isValidModuleName(owner) && nextRelativePath === physicalPath ? { owner, nextRelativePath } : null;
|
|
5042
4925
|
}
|
|
5043
|
-
function legacyRouteAdapterMatchesStub(
|
|
5044
|
-
const legacy = parseLegacyRouteAdapter(
|
|
4926
|
+
function legacyRouteAdapterMatchesStub(path5, source, stub) {
|
|
4927
|
+
const legacy = parseLegacyRouteAdapter(path5, source);
|
|
5045
4928
|
return legacy != null && legacy.owner === stub.owner;
|
|
5046
4929
|
}
|
|
5047
4930
|
var API_ROUTE_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/api/`;
|
|
5048
4931
|
var APP_DIR_PREFIX = `${APP_PACKAGE_DIR}/src/app/`;
|
|
5049
4932
|
var PAGE_FILE_SUFFIXES = ["/page.tsx", "/page.ts", "/page.jsx", "/page.js"];
|
|
5050
|
-
function expectedMarkerKindForPath(
|
|
5051
|
-
if (
|
|
5052
|
-
if (
|
|
5053
|
-
if (
|
|
5054
|
-
if (
|
|
5055
|
-
if (
|
|
5056
|
-
if (
|
|
5057
|
-
if (
|
|
5058
|
-
if (
|
|
4933
|
+
function expectedMarkerKindForPath(path5) {
|
|
4934
|
+
if (path5 === MODULES_GEN_PATH) return "registry";
|
|
4935
|
+
if (path5 === MODULE_CONTRIBUTIONS_GEN_PATH) return "contributions";
|
|
4936
|
+
if (path5 === MODULE_DATASTORES_GEN_PATH) return "datastores";
|
|
4937
|
+
if (path5 === MODULE_I18N_GEN_PATH) return "i18n";
|
|
4938
|
+
if (path5 === MODULE_INIT_SERVER_GEN_PATH) return "init";
|
|
4939
|
+
if (path5 === MODULE_SERVER_GEN_PATH) return "server";
|
|
4940
|
+
if (path5.startsWith(API_ROUTE_DIR_PREFIX) && path5.endsWith("/route.ts")) return "endpoint";
|
|
4941
|
+
if (path5.startsWith(APP_DIR_PREFIX) && !path5.startsWith(API_ROUTE_DIR_PREFIX) && PAGE_FILE_SUFFIXES.some((suffix) => path5.endsWith(suffix))) {
|
|
5059
4942
|
return "route";
|
|
5060
4943
|
}
|
|
5061
4944
|
return null;
|
|
5062
4945
|
}
|
|
5063
|
-
function markerCongruentWithPath(
|
|
5064
|
-
if (expectedMarkerKindForPath(
|
|
4946
|
+
function markerCongruentWithPath(path5, marker) {
|
|
4947
|
+
if (expectedMarkerKindForPath(path5) !== marker.kind) return false;
|
|
5065
4948
|
if (marker.kind === "route") {
|
|
5066
|
-
const derived = appPageFileToNextRelativePath(
|
|
4949
|
+
const derived = appPageFileToNextRelativePath(path5, APP_PACKAGE_DIR);
|
|
5067
4950
|
return derived != null && derived === marker.nextRelativePath;
|
|
5068
4951
|
}
|
|
5069
4952
|
return true;
|
|
@@ -5334,8 +5217,8 @@ function buildGeneratedModuleRegistry(input) {
|
|
|
5334
5217
|
function activeEnhancementPeers(registryModule) {
|
|
5335
5218
|
return new Set(registryModule.enhancements.filter((e) => e.active).map((e) => e.peer));
|
|
5336
5219
|
}
|
|
5337
|
-
function endpointBucketKey(
|
|
5338
|
-
return `${
|
|
5220
|
+
function endpointBucketKey(path5, owner, scope) {
|
|
5221
|
+
return `${path5}\0${owner}\0${scopeLabel(scope)}`;
|
|
5339
5222
|
}
|
|
5340
5223
|
function planRouteStubs(input) {
|
|
5341
5224
|
const endpointBuckets = /* @__PURE__ */ new Map();
|
|
@@ -5459,7 +5342,7 @@ function planRouteStubs(input) {
|
|
|
5459
5342
|
for (const entry of entries) {
|
|
5460
5343
|
const nextRelativePath = entry.nextRelativePath;
|
|
5461
5344
|
const destination = normalizeAppRouterDestinationPattern(nextRelativePath);
|
|
5462
|
-
const
|
|
5345
|
+
const path5 = routeEntryToAppPageFile(nextRelativePath);
|
|
5463
5346
|
const existing = routeDestinationOwners.get(destination);
|
|
5464
5347
|
if (existing) {
|
|
5465
5348
|
throw new Error(
|
|
@@ -5469,13 +5352,13 @@ function planRouteStubs(input) {
|
|
|
5469
5352
|
routeDestinationOwners.set(destination, {
|
|
5470
5353
|
owner: regMod.name,
|
|
5471
5354
|
nextRelativePath,
|
|
5472
|
-
path:
|
|
5355
|
+
path: path5
|
|
5473
5356
|
});
|
|
5474
5357
|
const importModule = routeEntryImport(regMod.name, nextRelativePath);
|
|
5475
5358
|
const analyzed = analyzeRouteEntryPath(nextRelativePath);
|
|
5476
5359
|
const mount = mounts.find((claim) => routeEntryCoveredByMountClaim(analyzed, claim));
|
|
5477
5360
|
routeStubs.push({
|
|
5478
|
-
path:
|
|
5361
|
+
path: path5,
|
|
5479
5362
|
owner: regMod.name,
|
|
5480
5363
|
nextRelativePath,
|
|
5481
5364
|
importModule,
|
|
@@ -5536,16 +5419,16 @@ function planCompositionArtifacts(input) {
|
|
|
5536
5419
|
...stubs.routeStubs.map((stub) => stub.path)
|
|
5537
5420
|
]);
|
|
5538
5421
|
const existingGeneratedPathSet = new Set(input.existingGeneratedPaths);
|
|
5539
|
-
const isProvenStaleDeletionCandidate = (
|
|
5540
|
-
if (
|
|
5541
|
-
if (COMPOSITION_RUNTIME_GEN_PATHS.includes(
|
|
5542
|
-
if (desiredPaths.has(
|
|
5543
|
-
if (!existingGeneratedPathSet.has(
|
|
5544
|
-
const legacy = parseLegacyApiAdapter(
|
|
5422
|
+
const isProvenStaleDeletionCandidate = (path5, content) => {
|
|
5423
|
+
if (path5 === MODULES_GEN_PATH) return false;
|
|
5424
|
+
if (COMPOSITION_RUNTIME_GEN_PATHS.includes(path5)) return false;
|
|
5425
|
+
if (desiredPaths.has(path5)) return false;
|
|
5426
|
+
if (!existingGeneratedPathSet.has(path5)) return false;
|
|
5427
|
+
const legacy = parseLegacyApiAdapter(path5, content);
|
|
5545
5428
|
if (legacy) return true;
|
|
5546
|
-
if (parseLegacyRouteAdapter(
|
|
5429
|
+
if (parseLegacyRouteAdapter(path5, content)) return true;
|
|
5547
5430
|
const marker = parseCompositionArtifactMarker(content);
|
|
5548
|
-
return marker != null && markerCongruentWithPath(
|
|
5431
|
+
return marker != null && markerCongruentWithPath(path5, marker);
|
|
5549
5432
|
};
|
|
5550
5433
|
const plannedByDestination = /* @__PURE__ */ new Map();
|
|
5551
5434
|
for (const stub of stubs.routeStubs) {
|
|
@@ -5699,13 +5582,13 @@ function planCompositionArtifacts(input) {
|
|
|
5699
5582
|
for (const stub of stubs.routeStubs) desired.set(stub.path, stub.content);
|
|
5700
5583
|
const endpointStubByPath = new Map(stubs.endpointStubs.map((stub) => [stub.path, stub]));
|
|
5701
5584
|
const routeStubByPath = new Map(stubs.routeStubs.map((stub) => [stub.path, stub]));
|
|
5702
|
-
for (const [
|
|
5703
|
-
const existing = input.existingFiles[
|
|
5585
|
+
for (const [path5] of desired) {
|
|
5586
|
+
const existing = input.existingFiles[path5];
|
|
5704
5587
|
if (existing == null) continue;
|
|
5705
5588
|
let marker = parseCompositionArtifactMarker(existing);
|
|
5706
|
-
const plannedEndpoint = endpointStubByPath.get(
|
|
5707
|
-
const plannedRoute = routeStubByPath.get(
|
|
5708
|
-
const legacy = parseLegacyApiAdapter(
|
|
5589
|
+
const plannedEndpoint = endpointStubByPath.get(path5);
|
|
5590
|
+
const plannedRoute = routeStubByPath.get(path5);
|
|
5591
|
+
const legacy = parseLegacyApiAdapter(path5, existing);
|
|
5709
5592
|
if (legacy && plannedEndpoint) {
|
|
5710
5593
|
marker = {
|
|
5711
5594
|
kind: "endpoint",
|
|
@@ -5713,66 +5596,66 @@ function planCompositionArtifacts(input) {
|
|
|
5713
5596
|
scope: plannedEndpoint.scope
|
|
5714
5597
|
};
|
|
5715
5598
|
}
|
|
5716
|
-
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(
|
|
5599
|
+
if (!marker && plannedRoute && legacyRouteAdapterMatchesStub(path5, existing, plannedRoute)) {
|
|
5717
5600
|
marker = {
|
|
5718
5601
|
kind: "route",
|
|
5719
5602
|
owner: plannedRoute.owner,
|
|
5720
5603
|
nextRelativePath: plannedRoute.nextRelativePath
|
|
5721
5604
|
};
|
|
5722
5605
|
}
|
|
5723
|
-
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(
|
|
5606
|
+
const isGenRuntimePath = COMPOSITION_RUNTIME_GEN_PATHS.includes(path5);
|
|
5724
5607
|
if (!marker) {
|
|
5725
5608
|
return {
|
|
5726
5609
|
ok: false,
|
|
5727
|
-
error:
|
|
5728
|
-
path:
|
|
5610
|
+
error: path5 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path5}` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path5}` : `[ModuleRail] composition artifacts: refusing to overwrite unmanaged route at ${path5}`,
|
|
5611
|
+
path: path5
|
|
5729
5612
|
};
|
|
5730
5613
|
}
|
|
5731
|
-
const expectedKind = expectedMarkerKindForPath(
|
|
5614
|
+
const expectedKind = expectedMarkerKindForPath(path5);
|
|
5732
5615
|
if (marker.kind !== expectedKind) {
|
|
5733
5616
|
return {
|
|
5734
5617
|
ok: false,
|
|
5735
|
-
error:
|
|
5736
|
-
path:
|
|
5618
|
+
error: path5 === MODULES_GEN_PATH ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged registry at ${path5} (incongruent marker ownership: found ${marker.kind} marker)` : isGenRuntimePath ? `[ModuleRail] composition artifacts: refusing to overwrite unmanaged generated file at ${path5} (incongruent marker ownership: found ${marker.kind} marker)` : `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path5} (expected ${expectedKind ?? "no"} marker, found ${marker.kind} marker)`,
|
|
5619
|
+
path: path5
|
|
5737
5620
|
};
|
|
5738
5621
|
}
|
|
5739
5622
|
if (marker.kind === "endpoint") {
|
|
5740
|
-
const stub = endpointStubByPath.get(
|
|
5623
|
+
const stub = endpointStubByPath.get(path5);
|
|
5741
5624
|
const ownerMatches = stub != null && marker.owner === stub.owner;
|
|
5742
5625
|
const scopeMatches = stub != null && scopeLabel(marker.scope) === scopeLabel(stub.scope);
|
|
5743
5626
|
if (!ownerMatches || !scopeMatches) {
|
|
5744
5627
|
return {
|
|
5745
5628
|
ok: false,
|
|
5746
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5747
|
-
path:
|
|
5629
|
+
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path5} (existing owner=${marker.owner} scope=${scopeLabel(marker.scope)} vs planned owner=${stub?.owner ?? "?"} scope=${stub ? scopeLabel(stub.scope) : "?"})`,
|
|
5630
|
+
path: path5,
|
|
5748
5631
|
owner: stub?.owner
|
|
5749
5632
|
};
|
|
5750
5633
|
}
|
|
5751
5634
|
} else if (marker.kind === "route") {
|
|
5752
|
-
const stub = routeStubByPath.get(
|
|
5635
|
+
const stub = routeStubByPath.get(path5);
|
|
5753
5636
|
const ownerMatches = stub != null && marker.owner === stub.owner;
|
|
5754
5637
|
const pathMatches = stub != null && marker.nextRelativePath === stub.nextRelativePath;
|
|
5755
5638
|
if (!ownerMatches || !pathMatches) {
|
|
5756
5639
|
return {
|
|
5757
5640
|
ok: false,
|
|
5758
|
-
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${
|
|
5759
|
-
path:
|
|
5641
|
+
error: `[ModuleRail] composition artifacts: refusing to overwrite incongruent marker ownership at ${path5} (existing owner=${marker.owner} path=${marker.nextRelativePath} vs planned owner=${stub?.owner ?? "?"} path=${stub?.nextRelativePath ?? "?"})`,
|
|
5642
|
+
path: path5,
|
|
5760
5643
|
owner: stub?.owner
|
|
5761
5644
|
};
|
|
5762
5645
|
}
|
|
5763
5646
|
}
|
|
5764
5647
|
}
|
|
5765
5648
|
const deletes = [];
|
|
5766
|
-
for (const
|
|
5767
|
-
const existing = input.existingFiles[
|
|
5649
|
+
for (const path5 of input.existingGeneratedPaths) {
|
|
5650
|
+
const existing = input.existingFiles[path5];
|
|
5768
5651
|
if (existing == null) continue;
|
|
5769
|
-
if (!isProvenStaleDeletionCandidate(
|
|
5652
|
+
if (!isProvenStaleDeletionCandidate(path5, existing)) {
|
|
5770
5653
|
continue;
|
|
5771
5654
|
}
|
|
5772
|
-
deletes.push(
|
|
5655
|
+
deletes.push(path5);
|
|
5773
5656
|
}
|
|
5774
5657
|
deletes.sort(compareNames2);
|
|
5775
|
-
const writes = [...desired.entries()].filter(([
|
|
5658
|
+
const writes = [...desired.entries()].filter(([path5, content]) => input.existingFiles[path5] !== content).map(([path5, content]) => ({ path: path5, content })).sort((a, b) => compareNames2(a.path, b.path));
|
|
5776
5659
|
return {
|
|
5777
5660
|
ok: true,
|
|
5778
5661
|
plan: {
|
|
@@ -5801,8 +5684,16 @@ async function checkoutParsesTypedStores(sandbox) {
|
|
|
5801
5684
|
try {
|
|
5802
5685
|
if (!await sandbox.fileExists(APP_PACKAGE_JSON_PATH)) return false;
|
|
5803
5686
|
const pkg = JSON.parse(await sandbox.readFile(APP_PACKAGE_JSON_PATH));
|
|
5804
|
-
|
|
5805
|
-
|
|
5687
|
+
return composeRangeParsesTypedStores(
|
|
5688
|
+
pkg.devDependencies?.[COMPOSE_PACKAGE] ?? pkg.dependencies?.[COMPOSE_PACKAGE]
|
|
5689
|
+
);
|
|
5690
|
+
} catch {
|
|
5691
|
+
return false;
|
|
5692
|
+
}
|
|
5693
|
+
}
|
|
5694
|
+
function composeRangeParsesTypedStores(range) {
|
|
5695
|
+
if (!range) return false;
|
|
5696
|
+
try {
|
|
5806
5697
|
const lowest = import_semver.default.minVersion(range);
|
|
5807
5698
|
return lowest != null && import_semver.default.gte(lowest, TYPED_STORES_MIN_COMPOSE_VERSION);
|
|
5808
5699
|
} catch {
|
|
@@ -6034,6 +5925,50 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
|
|
|
6034
5925
|
return dirs;
|
|
6035
5926
|
}
|
|
6036
5927
|
|
|
5928
|
+
// ../../packages/lib/src/shared/module-composition.ts
|
|
5929
|
+
var import_zod7 = require("zod");
|
|
5930
|
+
var creationSourceBlankSchema = import_zod7.z.object({
|
|
5931
|
+
type: import_zod7.z.literal("blank")
|
|
5932
|
+
});
|
|
5933
|
+
var creationSourceBlueprintSchema = import_zod7.z.object({
|
|
5934
|
+
type: import_zod7.z.literal("blueprint"),
|
|
5935
|
+
/** Internal template/fork project id — keep template naming in identifiers. */
|
|
5936
|
+
blueprintProjectId: import_zod7.z.string().uuid(),
|
|
5937
|
+
/**
|
|
5938
|
+
* `null`/absent means every feature Module. `[]` is refused: the platform
|
|
5939
|
+
* serialises it to an empty `NEXT_PUBLIC_STARDECK_ENABLED_MODULES`, which the
|
|
5940
|
+
* enablement parser reads back as "all" (`module-enablement.ts`) — the
|
|
5941
|
+
* opposite of what an empty selection means.
|
|
5942
|
+
*/
|
|
5943
|
+
enabledModules: import_zod7.z.array(moduleNameSchema).min(1, "Enable at least one Module").max(100).nullable().optional()
|
|
5944
|
+
});
|
|
5945
|
+
var creationSourceSchema = import_zod7.z.discriminatedUnion("type", [
|
|
5946
|
+
creationSourceBlankSchema,
|
|
5947
|
+
creationSourceBlueprintSchema
|
|
5948
|
+
]);
|
|
5949
|
+
var moduleInstallHandoffSchema = import_zod7.z.object({
|
|
5950
|
+
moduleName: import_zod7.z.string().min(1),
|
|
5951
|
+
homeProjectId: import_zod7.z.string().uuid(),
|
|
5952
|
+
installedVersion: import_zod7.z.string().min(1),
|
|
5953
|
+
baselineSha: import_zod7.z.string().min(1),
|
|
5954
|
+
mounts: import_zod7.z.array(
|
|
5955
|
+
import_zod7.z.object({
|
|
5956
|
+
group: import_zod7.z.string(),
|
|
5957
|
+
segment: import_zod7.z.string()
|
|
5958
|
+
})
|
|
5959
|
+
),
|
|
5960
|
+
integration: import_zod7.z.array(import_zod7.z.string()),
|
|
5961
|
+
workflows: import_zod7.z.array(import_zod7.z.string()),
|
|
5962
|
+
prunedEnhancements: import_zod7.z.array(import_zod7.z.string()),
|
|
5963
|
+
restoredEnhancements: import_zod7.z.array(import_zod7.z.string()).optional(),
|
|
5964
|
+
permissions: import_zod7.z.array(
|
|
5965
|
+
import_zod7.z.object({
|
|
5966
|
+
key: import_zod7.z.string().min(1),
|
|
5967
|
+
description: import_zod7.z.string().min(1)
|
|
5968
|
+
})
|
|
5969
|
+
).optional()
|
|
5970
|
+
});
|
|
5971
|
+
|
|
6037
5972
|
// ../../packages/lib/src/shared/schema-ops.ts
|
|
6038
5973
|
var import_zod8 = require("zod");
|
|
6039
5974
|
var columnSpecSchema = import_zod8.z.object({
|
|
@@ -6247,11 +6182,11 @@ function countedCompositionContext(context) {
|
|
|
6247
6182
|
counts.sandboxExecs += 1;
|
|
6248
6183
|
return context.sandbox.exec(...args);
|
|
6249
6184
|
},
|
|
6250
|
-
readFile: (
|
|
6185
|
+
readFile: (path5) => {
|
|
6251
6186
|
counts.sandboxReads += 1;
|
|
6252
|
-
return context.sandbox.readFile(
|
|
6187
|
+
return context.sandbox.readFile(path5);
|
|
6253
6188
|
},
|
|
6254
|
-
fileExists: (
|
|
6189
|
+
fileExists: (path5) => context.sandbox.fileExists(path5),
|
|
6255
6190
|
fetchRemoteRef: (...args) => context.sandbox.fetchRemoteRef(...args),
|
|
6256
6191
|
addDependencies: (specs) => context.sandbox.addDependencies(specs)
|
|
6257
6192
|
}
|
|
@@ -6463,14 +6398,14 @@ async function discoverModuleRouteEntries(context, moduleName) {
|
|
|
6463
6398
|
}
|
|
6464
6399
|
return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
|
|
6465
6400
|
}
|
|
6466
|
-
async function writeSandboxFileAtomic(target,
|
|
6467
|
-
const dir = dirnamePosix(
|
|
6468
|
-
const tmp = `${
|
|
6401
|
+
async function writeSandboxFileAtomic(target, path5, content) {
|
|
6402
|
+
const dir = dirnamePosix(path5);
|
|
6403
|
+
const tmp = `${path5}.tmp.${target.runId}`;
|
|
6469
6404
|
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
6470
6405
|
const cmd = [
|
|
6471
6406
|
`mkdir -p ${shellQuote(dir)}`,
|
|
6472
6407
|
`printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
|
|
6473
|
-
`mv -f ${shellQuote(tmp)} ${shellQuote(
|
|
6408
|
+
`mv -f ${shellQuote(tmp)} ${shellQuote(path5)}`
|
|
6474
6409
|
].join(" && ");
|
|
6475
6410
|
const result = await target.sandbox.exec(cmd, { raiseOnError: false });
|
|
6476
6411
|
if (result.exitCode !== 0) {
|
|
@@ -6485,14 +6420,14 @@ async function writeSandboxFileAtomic(target, path3, content) {
|
|
|
6485
6420
|
}
|
|
6486
6421
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6487
6422
|
throw new Error(
|
|
6488
|
-
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(
|
|
6423
|
+
`[ModuleRail] composition artifacts: failed to write ${redactSecrets(path5)}: ${redactSecrets(detail)}`
|
|
6489
6424
|
);
|
|
6490
6425
|
}
|
|
6491
6426
|
}
|
|
6492
6427
|
async function collectAbsentWriteParentDirs(context, writePaths) {
|
|
6493
6428
|
const absent = /* @__PURE__ */ new Set();
|
|
6494
|
-
for (const
|
|
6495
|
-
let dir = dirnamePosix(
|
|
6429
|
+
for (const path5 of writePaths) {
|
|
6430
|
+
let dir = dirnamePosix(path5);
|
|
6496
6431
|
while (dir !== "." && dir !== "") {
|
|
6497
6432
|
if (absent.has(dir)) {
|
|
6498
6433
|
dir = dirnamePosix(dir);
|
|
@@ -6529,12 +6464,12 @@ async function removeAbsentWriteParentDirsOnRollback(context, dirsDeepestFirst)
|
|
|
6529
6464
|
);
|
|
6530
6465
|
}
|
|
6531
6466
|
}
|
|
6532
|
-
async function deleteSandboxFile(context,
|
|
6533
|
-
const result = await context.sandbox.exec(`rm -f ${shellQuote(
|
|
6467
|
+
async function deleteSandboxFile(context, path5) {
|
|
6468
|
+
const result = await context.sandbox.exec(`rm -f ${shellQuote(path5)}`, { raiseOnError: false });
|
|
6534
6469
|
if (result.exitCode !== 0) {
|
|
6535
6470
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
6536
6471
|
throw new Error(
|
|
6537
|
-
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(
|
|
6472
|
+
`[ModuleRail] composition artifacts: failed to delete ${redactSecrets(path5)}: ${redactSecrets(detail)}`
|
|
6538
6473
|
);
|
|
6539
6474
|
}
|
|
6540
6475
|
}
|
|
@@ -6601,37 +6536,37 @@ async function applyCompositionArtifactMutations(context, plan, existingFiles) {
|
|
|
6601
6536
|
await writeSandboxFileAtomic(context, write.path, write.content);
|
|
6602
6537
|
appliedWrites.push({ path: write.path, previous });
|
|
6603
6538
|
}
|
|
6604
|
-
for (const
|
|
6605
|
-
const previous = existingFiles[
|
|
6539
|
+
for (const path5 of plan.deletes) {
|
|
6540
|
+
const previous = existingFiles[path5];
|
|
6606
6541
|
if (previous == null) continue;
|
|
6607
|
-
await deleteSandboxFile(context,
|
|
6608
|
-
appliedDeletes.push({ path:
|
|
6542
|
+
await deleteSandboxFile(context, path5);
|
|
6543
|
+
appliedDeletes.push({ path: path5, previous });
|
|
6609
6544
|
}
|
|
6610
6545
|
} catch (error) {
|
|
6611
6546
|
console.log(
|
|
6612
6547
|
`[ModuleRail] runId=${context.runId} composition artifacts mid-apply failure; rolling back writes=${appliedWrites.length} deletes=${appliedDeletes.length} newDirs=${newlyCreatedParentDirs.length}`
|
|
6613
6548
|
);
|
|
6614
|
-
for (const { path:
|
|
6549
|
+
for (const { path: path5, previous } of [...appliedDeletes].reverse()) {
|
|
6615
6550
|
try {
|
|
6616
|
-
await writeSandboxFileAtomic(context,
|
|
6551
|
+
await writeSandboxFileAtomic(context, path5, previous);
|
|
6617
6552
|
} catch (rollbackError) {
|
|
6618
6553
|
console.log(
|
|
6619
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(
|
|
6554
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback restore failed for ${redactSecrets(path5)}: ${redactSecrets(
|
|
6620
6555
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6621
6556
|
)}`
|
|
6622
6557
|
);
|
|
6623
6558
|
}
|
|
6624
6559
|
}
|
|
6625
|
-
for (const { path:
|
|
6560
|
+
for (const { path: path5, previous } of [...appliedWrites].reverse()) {
|
|
6626
6561
|
try {
|
|
6627
6562
|
if (previous == null) {
|
|
6628
|
-
await deleteSandboxFile(context,
|
|
6563
|
+
await deleteSandboxFile(context, path5);
|
|
6629
6564
|
} else {
|
|
6630
|
-
await writeSandboxFileAtomic(context,
|
|
6565
|
+
await writeSandboxFileAtomic(context, path5, previous);
|
|
6631
6566
|
}
|
|
6632
6567
|
} catch (rollbackError) {
|
|
6633
6568
|
console.log(
|
|
6634
|
-
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(
|
|
6569
|
+
`[ModuleRail] runId=${context.runId} composition artifacts rollback write failed for ${redactSecrets(path5)}: ${redactSecrets(
|
|
6635
6570
|
rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
6636
6571
|
)}`
|
|
6637
6572
|
);
|
|
@@ -6665,39 +6600,39 @@ async function listExistingGeneratedStubPaths(context) {
|
|
|
6665
6600
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
6666
6601
|
);
|
|
6667
6602
|
const presentPaths = [];
|
|
6668
|
-
for (const
|
|
6603
|
+
for (const path5 of candidates) {
|
|
6669
6604
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
6670
|
-
|
|
6671
|
-
kinds.get(
|
|
6605
|
+
path5,
|
|
6606
|
+
kinds.get(path5) ?? "unreadable"
|
|
6672
6607
|
);
|
|
6673
|
-
if (presence === "file") presentPaths.push(
|
|
6608
|
+
if (presence === "file") presentPaths.push(path5);
|
|
6674
6609
|
}
|
|
6675
6610
|
let contents;
|
|
6676
6611
|
try {
|
|
6677
6612
|
contents = await readFilesMany(context, presentPaths);
|
|
6678
6613
|
} catch (error) {
|
|
6679
|
-
const
|
|
6614
|
+
const path5 = readFailurePath(error);
|
|
6680
6615
|
throw new Error(
|
|
6681
|
-
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(
|
|
6616
|
+
`[ModuleRail] composition artifacts: cannot read candidate stub ${redactSecrets(path5)}: ${redactSecrets(
|
|
6682
6617
|
error instanceof Error ? error.message : String(error)
|
|
6683
6618
|
)}`
|
|
6684
6619
|
);
|
|
6685
6620
|
}
|
|
6686
6621
|
const owned = [];
|
|
6687
|
-
for (const
|
|
6688
|
-
const content = contents.get(
|
|
6622
|
+
for (const path5 of presentPaths) {
|
|
6623
|
+
const content = contents.get(path5);
|
|
6689
6624
|
const marker = parseCompositionArtifactMarker(content);
|
|
6690
|
-
if (parseLegacyApiAdapter(
|
|
6691
|
-
owned.push(
|
|
6625
|
+
if (parseLegacyApiAdapter(path5, content) || parseLegacyRouteAdapter(path5, content)) {
|
|
6626
|
+
owned.push(path5);
|
|
6692
6627
|
continue;
|
|
6693
6628
|
}
|
|
6694
6629
|
if (marker == null) continue;
|
|
6695
|
-
if (!markerCongruentWithPath(
|
|
6630
|
+
if (!markerCongruentWithPath(path5, marker)) {
|
|
6696
6631
|
throw new Error(
|
|
6697
|
-
`[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(
|
|
6632
|
+
`[ModuleRail] composition artifacts: incongruent marker at ${redactSecrets(path5)} (found ${marker.kind} marker on a path shaped for ${expectedMarkerKindForPath(path5) ?? "no"} artifacts; refusing to reconcile a swapped marker)`
|
|
6698
6633
|
);
|
|
6699
6634
|
}
|
|
6700
|
-
owned.push(
|
|
6635
|
+
owned.push(path5);
|
|
6701
6636
|
}
|
|
6702
6637
|
return owned;
|
|
6703
6638
|
}
|
|
@@ -6830,7 +6765,7 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6830
6765
|
{ raiseOnError: false }
|
|
6831
6766
|
);
|
|
6832
6767
|
if (result.exitCode !== 0) {
|
|
6833
|
-
for (const
|
|
6768
|
+
for (const path5 of chunk) kinds.set(path5, "unreadable");
|
|
6834
6769
|
continue;
|
|
6835
6770
|
}
|
|
6836
6771
|
const lines = result.output.split("\n").filter(Boolean);
|
|
@@ -6841,31 +6776,31 @@ async function classifySandboxPathPresenceKindMany(context, paths, probeToken) {
|
|
|
6841
6776
|
}
|
|
6842
6777
|
} else {
|
|
6843
6778
|
for (const line of lines) {
|
|
6844
|
-
const [rawKind,
|
|
6779
|
+
const [rawKind, path5] = line.split(" ", 2);
|
|
6845
6780
|
const kind = rawKind?.replace(/^entry-kind:/, "");
|
|
6846
|
-
if (
|
|
6847
|
-
kinds.set(
|
|
6781
|
+
if (path5 && chunk.includes(path5) && CONVENTIONAL_ENTRY_PRESENCE_KINDS.includes(kind ?? "")) {
|
|
6782
|
+
kinds.set(path5, kind);
|
|
6848
6783
|
}
|
|
6849
6784
|
}
|
|
6850
6785
|
}
|
|
6851
|
-
for (const
|
|
6852
|
-
if (!kinds.has(
|
|
6786
|
+
for (const path5 of chunk) {
|
|
6787
|
+
if (!kinds.has(path5)) kinds.set(path5, "unreadable");
|
|
6853
6788
|
}
|
|
6854
6789
|
}
|
|
6855
6790
|
return kinds;
|
|
6856
6791
|
}
|
|
6857
|
-
async function classifySandboxPathPresenceKind(context,
|
|
6858
|
-
return (await classifySandboxPathPresenceKindMany(context, [
|
|
6792
|
+
async function classifySandboxPathPresenceKind(context, path5, probeToken) {
|
|
6793
|
+
return (await classifySandboxPathPresenceKindMany(context, [path5], probeToken)).get(path5);
|
|
6859
6794
|
}
|
|
6860
6795
|
var LOCALE_JSON_KIND_PROBE_TOKEN = "locale-json-kind";
|
|
6861
|
-
function assertRealLocaleJsonFilePresence(
|
|
6796
|
+
function assertRealLocaleJsonFilePresence(path5, presence) {
|
|
6862
6797
|
if (presence === "file") return;
|
|
6863
6798
|
if (presence === "absent") {
|
|
6864
|
-
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${
|
|
6799
|
+
throw new Error(`[ModuleRail] composition artifacts: locale file missing at ${path5}`);
|
|
6865
6800
|
}
|
|
6866
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
6801
|
+
const presenceError = formatConventionalEntryPresenceError(path5, presence);
|
|
6867
6802
|
throw new Error(
|
|
6868
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
6803
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path5} is not a real locale JSON file`}`
|
|
6869
6804
|
);
|
|
6870
6805
|
}
|
|
6871
6806
|
function splitTaggedLine(line) {
|
|
@@ -6940,11 +6875,11 @@ async function listLocaleBasenamesIfDirMany(context, dirs) {
|
|
|
6940
6875
|
}
|
|
6941
6876
|
async function readFilesMany(context, paths) {
|
|
6942
6877
|
const sortedPaths = [...paths].sort((a, b) => a.localeCompare(b));
|
|
6943
|
-
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (
|
|
6878
|
+
const settled = await mapConcurrent(sortedPaths, SANDBOX_READ_CONCURRENCY, async (path5) => {
|
|
6944
6879
|
try {
|
|
6945
|
-
return { path:
|
|
6880
|
+
return { path: path5, value: await context.sandbox.readFile(path5) };
|
|
6946
6881
|
} catch (error) {
|
|
6947
|
-
return { path:
|
|
6882
|
+
return { path: path5, error };
|
|
6948
6883
|
}
|
|
6949
6884
|
});
|
|
6950
6885
|
const files = /* @__PURE__ */ new Map();
|
|
@@ -6967,58 +6902,58 @@ function readFailurePath(error) {
|
|
|
6967
6902
|
var CONVENTIONAL_ENTRY_KIND_PROBE_TOKEN = "conventional-entry-kind";
|
|
6968
6903
|
var SERVER_REGISTRATIONS_FILE = "server/registrations.ts";
|
|
6969
6904
|
var COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN = "composition-artifact-kind";
|
|
6970
|
-
async function classifyCompositionArtifactPathKind(context,
|
|
6971
|
-
return classifySandboxPathPresenceKind(context,
|
|
6905
|
+
async function classifyCompositionArtifactPathKind(context, path5) {
|
|
6906
|
+
return classifySandboxPathPresenceKind(context, path5, COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN);
|
|
6972
6907
|
}
|
|
6973
|
-
function assertRealCompositionArtifactFilePresence(
|
|
6908
|
+
function assertRealCompositionArtifactFilePresence(path5, presence) {
|
|
6974
6909
|
if (presence === "file") return "file";
|
|
6975
6910
|
if (presence === "absent") return "absent";
|
|
6976
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
6911
|
+
const presenceError = formatConventionalEntryPresenceError(path5, presence);
|
|
6977
6912
|
throw new Error(
|
|
6978
|
-
`[ModuleRail] composition artifacts: ${presenceError ?? `${
|
|
6913
|
+
`[ModuleRail] composition artifacts: ${presenceError ?? `${path5} is not a real composition artifact file`}`
|
|
6979
6914
|
);
|
|
6980
6915
|
}
|
|
6981
|
-
async function readConventionalEntryIfRealFile(
|
|
6982
|
-
const presence = presences.get(
|
|
6983
|
-
const presenceError = formatConventionalEntryPresenceError(
|
|
6916
|
+
async function readConventionalEntryIfRealFile(path5, kind, presences, files) {
|
|
6917
|
+
const presence = presences.get(path5) ?? "unreadable";
|
|
6918
|
+
const presenceError = formatConventionalEntryPresenceError(path5, presence);
|
|
6984
6919
|
if (presenceError) {
|
|
6985
6920
|
throw new Error(`[ModuleRail] composition artifacts: ${presenceError}`);
|
|
6986
6921
|
}
|
|
6987
6922
|
if (presence === "absent") {
|
|
6988
6923
|
return null;
|
|
6989
6924
|
}
|
|
6990
|
-
const source = files.get(
|
|
6925
|
+
const source = files.get(path5);
|
|
6991
6926
|
switch (kind) {
|
|
6992
6927
|
case "contributions":
|
|
6993
|
-
assertContributionsExport(source,
|
|
6994
|
-
assertClientSafeConventionalEntryImports(source,
|
|
6928
|
+
assertContributionsExport(source, path5);
|
|
6929
|
+
assertClientSafeConventionalEntryImports(source, path5, "contributions");
|
|
6995
6930
|
break;
|
|
6996
6931
|
case "slotCatalogs":
|
|
6997
|
-
assertSlotCatalogsExport(source,
|
|
6998
|
-
assertClientSafeConventionalEntryImports(source,
|
|
6932
|
+
assertSlotCatalogsExport(source, path5);
|
|
6933
|
+
assertClientSafeConventionalEntryImports(source, path5, "slotCatalogs");
|
|
6999
6934
|
break;
|
|
7000
6935
|
case "initializeEnhancement":
|
|
7001
|
-
assertInitializeEnhancementExport(source,
|
|
6936
|
+
assertInitializeEnhancementExport(source, path5);
|
|
7002
6937
|
break;
|
|
7003
6938
|
}
|
|
7004
6939
|
return source;
|
|
7005
6940
|
}
|
|
7006
6941
|
function hasRealServerRegistrations(modulePath, presences) {
|
|
7007
|
-
const
|
|
7008
|
-
return assertRealCompositionArtifactFilePresence(
|
|
6942
|
+
const path5 = `${modulePath}/${SERVER_REGISTRATIONS_FILE}`;
|
|
6943
|
+
return assertRealCompositionArtifactFilePresence(path5, presences.get(path5) ?? "unreadable") === "file";
|
|
7009
6944
|
}
|
|
7010
|
-
async function readJsonObjectFile(
|
|
7011
|
-
const raw = files.get(
|
|
6945
|
+
async function readJsonObjectFile(path5, files) {
|
|
6946
|
+
const raw = files.get(path5);
|
|
7012
6947
|
let parsed;
|
|
7013
6948
|
try {
|
|
7014
6949
|
parsed = JSON.parse(raw);
|
|
7015
6950
|
} catch (error) {
|
|
7016
6951
|
throw new Error(
|
|
7017
|
-
`[ModuleRail] composition artifacts: invalid JSON at ${
|
|
6952
|
+
`[ModuleRail] composition artifacts: invalid JSON at ${path5}: ${error instanceof Error ? error.message : String(error)}`
|
|
7018
6953
|
);
|
|
7019
6954
|
}
|
|
7020
6955
|
if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
7021
|
-
throw new Error(`[ModuleRail] composition artifacts: ${
|
|
6956
|
+
throw new Error(`[ModuleRail] composition artifacts: ${path5} root must be an object`);
|
|
7022
6957
|
}
|
|
7023
6958
|
return parsed;
|
|
7024
6959
|
}
|
|
@@ -7067,11 +7002,11 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7067
7002
|
classifySandboxPathPresenceKindMany(context, localePaths, LOCALE_JSON_KIND_PROBE_TOKEN)
|
|
7068
7003
|
]);
|
|
7069
7004
|
const presentConventionalPaths = [];
|
|
7070
|
-
for (const
|
|
7071
|
-
const presence = conventionalPresences.get(
|
|
7072
|
-
if (presence === "file") presentConventionalPaths.push(
|
|
7005
|
+
for (const path5 of conventionalPaths) {
|
|
7006
|
+
const presence = conventionalPresences.get(path5) ?? "unreadable";
|
|
7007
|
+
if (presence === "file") presentConventionalPaths.push(path5);
|
|
7073
7008
|
}
|
|
7074
|
-
const presentLocalePaths = localePaths.filter((
|
|
7009
|
+
const presentLocalePaths = localePaths.filter((path5) => localePresences.get(path5) === "file");
|
|
7075
7010
|
const files = await readFilesMany(context, [...presentConventionalPaths, ...presentLocalePaths]);
|
|
7076
7011
|
const substrateLocales = [];
|
|
7077
7012
|
for (const locale of substrateNames) {
|
|
@@ -7169,8 +7104,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7169
7104
|
});
|
|
7170
7105
|
if (rootLocales.length > 0) {
|
|
7171
7106
|
for (const locale of rootLocales) {
|
|
7172
|
-
const
|
|
7173
|
-
assertRealLocaleJsonFilePresence(
|
|
7107
|
+
const path5 = `${modulePath}/i18n/${locale}.json`;
|
|
7108
|
+
assertRealLocaleJsonFilePresence(path5, localePresences.get(path5) ?? "unreadable");
|
|
7174
7109
|
}
|
|
7175
7110
|
const enMessages = await readJsonObjectFile(`${modulePath}/i18n/en.json`, files);
|
|
7176
7111
|
const localeMessages = { en: enMessages };
|
|
@@ -7194,8 +7129,8 @@ async function collectCompositionEntryFacts(context, registry) {
|
|
|
7194
7129
|
if (!enh.active || enh.locales.length === 0) continue;
|
|
7195
7130
|
const peerDir = `${modulePath}/enhancements/${enh.peer}`;
|
|
7196
7131
|
for (const locale of enh.locales) {
|
|
7197
|
-
const
|
|
7198
|
-
assertRealLocaleJsonFilePresence(
|
|
7132
|
+
const path5 = `${peerDir}/i18n/${locale}.json`;
|
|
7133
|
+
assertRealLocaleJsonFilePresence(path5, localePresences.get(path5) ?? "unreadable");
|
|
7199
7134
|
}
|
|
7200
7135
|
const enMessages = await readJsonObjectFile(`${peerDir}/i18n/en.json`, files);
|
|
7201
7136
|
const localeMessages = { en: enMessages };
|
|
@@ -7396,16 +7331,16 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7396
7331
|
for (const stub of probePlan.plan.routeStubs) candidatePaths.add(stub.path);
|
|
7397
7332
|
const existingFiles = {};
|
|
7398
7333
|
const remainingCandidatePaths = [];
|
|
7399
|
-
for (const
|
|
7400
|
-
if (Object.prototype.hasOwnProperty.call(existingAppPages,
|
|
7401
|
-
existingFiles[
|
|
7334
|
+
for (const path5 of [...candidatePaths].sort((a, b) => a.localeCompare(b))) {
|
|
7335
|
+
if (Object.prototype.hasOwnProperty.call(existingAppPages, path5)) {
|
|
7336
|
+
existingFiles[path5] = existingAppPages[path5];
|
|
7402
7337
|
continue;
|
|
7403
7338
|
}
|
|
7404
|
-
if (Object.prototype.hasOwnProperty.call(existingAppRoutes,
|
|
7405
|
-
existingFiles[
|
|
7339
|
+
if (Object.prototype.hasOwnProperty.call(existingAppRoutes, path5)) {
|
|
7340
|
+
existingFiles[path5] = existingAppRoutes[path5];
|
|
7406
7341
|
continue;
|
|
7407
7342
|
}
|
|
7408
|
-
remainingCandidatePaths.push(
|
|
7343
|
+
remainingCandidatePaths.push(path5);
|
|
7409
7344
|
}
|
|
7410
7345
|
const candidateKinds = await classifySandboxPathPresenceKindMany(
|
|
7411
7346
|
context,
|
|
@@ -7413,12 +7348,12 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7413
7348
|
COMPOSITION_ARTIFACT_KIND_PROBE_TOKEN
|
|
7414
7349
|
);
|
|
7415
7350
|
const presentCandidatePaths = [];
|
|
7416
|
-
for (const
|
|
7351
|
+
for (const path5 of remainingCandidatePaths) {
|
|
7417
7352
|
const presence = assertRealCompositionArtifactFilePresence(
|
|
7418
|
-
|
|
7419
|
-
candidateKinds.get(
|
|
7353
|
+
path5,
|
|
7354
|
+
candidateKinds.get(path5) ?? "unreadable"
|
|
7420
7355
|
);
|
|
7421
|
-
if (presence === "file") presentCandidatePaths.push(
|
|
7356
|
+
if (presence === "file") presentCandidatePaths.push(path5);
|
|
7422
7357
|
}
|
|
7423
7358
|
try {
|
|
7424
7359
|
Object.assign(
|
|
@@ -7426,9 +7361,9 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7426
7361
|
Object.fromEntries(await readFilesMany(context, presentCandidatePaths))
|
|
7427
7362
|
);
|
|
7428
7363
|
} catch (error) {
|
|
7429
|
-
const
|
|
7364
|
+
const path5 = readFailurePath(error);
|
|
7430
7365
|
throw new Error(
|
|
7431
|
-
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(
|
|
7366
|
+
`[ModuleRail] composition artifacts: cannot read ${redactSecrets(path5)} during preflight: ${redactSecrets(
|
|
7432
7367
|
error instanceof Error ? error.message : String(error)
|
|
7433
7368
|
)}`
|
|
7434
7369
|
);
|
|
@@ -7497,14 +7432,142 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
7497
7432
|
var HOME_I18N_SUBSTRATE_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/substrate`;
|
|
7498
7433
|
var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
|
|
7499
7434
|
|
|
7435
|
+
// src/index.ts
|
|
7436
|
+
var import_typescript6 = __toESM(require("typescript"));
|
|
7437
|
+
|
|
7438
|
+
// src/module-boundaries.ts
|
|
7439
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
7440
|
+
var import_node_path2 = __toESM(require("path"));
|
|
7441
|
+
var import_typescript5 = __toESM(require("typescript"));
|
|
7442
|
+
var SOURCE_FILE = /\.[cm]?[jt]sx?$/;
|
|
7443
|
+
var RESOLVE_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
7444
|
+
var DATA_FILE = /\.(?:sql|json)$/;
|
|
7445
|
+
function dataPathsIn(fileName, source) {
|
|
7446
|
+
if (!/\.(?:sql|json)["'`]/.test(source)) return [];
|
|
7447
|
+
const paths = [];
|
|
7448
|
+
const visit = (node) => {
|
|
7449
|
+
if ((import_typescript5.default.isStringLiteralLike(node) || import_typescript5.default.isTemplateTail(node)) && DATA_FILE.test(node.text)) {
|
|
7450
|
+
paths.push(node.text);
|
|
7451
|
+
}
|
|
7452
|
+
import_typescript5.default.forEachChild(node, visit);
|
|
7453
|
+
};
|
|
7454
|
+
visit(import_typescript5.default.createSourceFile(fileName, source, import_typescript5.default.ScriptTarget.Latest, false));
|
|
7455
|
+
return paths;
|
|
7456
|
+
}
|
|
7457
|
+
function scanModule(modulesDir, module2) {
|
|
7458
|
+
const scanned = /* @__PURE__ */ new Map();
|
|
7459
|
+
const moduleDir = import_node_path2.default.join(modulesDir, module2);
|
|
7460
|
+
if (!import_node_fs2.default.existsSync(moduleDir)) return scanned;
|
|
7461
|
+
const files = import_node_fs2.default.readdirSync(moduleDir, { recursive: true, encoding: "utf8" }).filter((relative) => SOURCE_FILE.test(relative) && !relative.includes("node_modules"));
|
|
7462
|
+
for (const relative of files) {
|
|
7463
|
+
const absolute = import_node_path2.default.join(moduleDir, relative);
|
|
7464
|
+
if (!import_node_fs2.default.statSync(absolute).isFile()) continue;
|
|
7465
|
+
const source = import_node_fs2.default.readFileSync(absolute, "utf8");
|
|
7466
|
+
const specifiers = import_typescript5.default.preProcessFile(source, true, true).importedFiles.map((f) => f.fileName);
|
|
7467
|
+
const dataPaths = dataPathsIn(absolute, source);
|
|
7468
|
+
const targets = [...specifiers, ...dataPaths].flatMap((spec) => {
|
|
7469
|
+
if (spec.startsWith("@/modules/"))
|
|
7470
|
+
return [import_node_path2.default.join(modulesDir, spec.slice("@/modules/".length))];
|
|
7471
|
+
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
7472
|
+
return [import_node_path2.default.resolve(import_node_path2.default.dirname(absolute), spec)];
|
|
7473
|
+
}
|
|
7474
|
+
return [];
|
|
7475
|
+
});
|
|
7476
|
+
scanned.set(absolute, {
|
|
7477
|
+
module: module2,
|
|
7478
|
+
relative: relative.split(import_node_path2.default.sep).join("/"),
|
|
7479
|
+
targets,
|
|
7480
|
+
dataPaths
|
|
7481
|
+
});
|
|
7482
|
+
}
|
|
7483
|
+
return scanned;
|
|
7484
|
+
}
|
|
7485
|
+
function findModuleBoundaryViolations(modulesDir, peersByModule, { pruning = false } = {}) {
|
|
7486
|
+
const files = /* @__PURE__ */ new Map();
|
|
7487
|
+
for (const [module2, peers] of Object.entries(peersByModule)) {
|
|
7488
|
+
if (peers.length === 0) continue;
|
|
7489
|
+
for (const [absolute, file] of scanModule(modulesDir, module2)) files.set(absolute, file);
|
|
7490
|
+
}
|
|
7491
|
+
const exempt = (file, peer) => (pruning ? peersByModule[file.module] ?? [] : [peer]).some(
|
|
7492
|
+
(q) => file.relative.startsWith(`enhancements/${q}/`)
|
|
7493
|
+
);
|
|
7494
|
+
const goneWith = (target, peer) => {
|
|
7495
|
+
const relative = import_node_path2.default.relative(modulesDir, target).split(import_node_path2.default.sep).join("/");
|
|
7496
|
+
return relative === peer || relative.startsWith(`${peer}/`) || new RegExp(`^[^/]+/enhancements/${peer}(/|$)`).test(relative);
|
|
7497
|
+
};
|
|
7498
|
+
const tainted = /* @__PURE__ */ new Map();
|
|
7499
|
+
const queue = [];
|
|
7500
|
+
const taint = (absolute, peer) => {
|
|
7501
|
+
const peers = tainted.get(absolute) ?? /* @__PURE__ */ new Set();
|
|
7502
|
+
if (peers.has(peer)) return;
|
|
7503
|
+
peers.add(peer);
|
|
7504
|
+
tainted.set(absolute, peers);
|
|
7505
|
+
queue.push([absolute, peer]);
|
|
7506
|
+
};
|
|
7507
|
+
const importers = /* @__PURE__ */ new Map();
|
|
7508
|
+
for (const [absolute, file] of files) {
|
|
7509
|
+
for (const target of file.targets) {
|
|
7510
|
+
const hit = RESOLVE_SUFFIXES.map((suffix) => target + suffix).find((c) => files.has(c));
|
|
7511
|
+
if (hit) importers.set(hit, [...importers.get(hit) ?? [], absolute]);
|
|
7512
|
+
}
|
|
7513
|
+
for (const peer of peersByModule[file.module] ?? []) {
|
|
7514
|
+
if (exempt(file, peer)) continue;
|
|
7515
|
+
if (file.targets.some((target) => goneWith(target, peer)) || file.dataPaths.some(
|
|
7516
|
+
(p) => p.startsWith(`modules/${peer}/`) || p.includes(`/modules/${peer}/`)
|
|
7517
|
+
)) {
|
|
7518
|
+
taint(absolute, peer);
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7522
|
+
for (let next = queue.shift(); next; next = queue.shift()) {
|
|
7523
|
+
const [target, peer] = next;
|
|
7524
|
+
for (const importer of importers.get(target) ?? []) {
|
|
7525
|
+
const file = files.get(importer);
|
|
7526
|
+
if ((peersByModule[file.module] ?? []).includes(peer) && !exempt(file, peer)) {
|
|
7527
|
+
taint(importer, peer);
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
const violations = [];
|
|
7532
|
+
for (const [absolute, peers] of tainted) {
|
|
7533
|
+
const file = files.get(absolute);
|
|
7534
|
+
for (const peer of peers) {
|
|
7535
|
+
violations.push({ file: `${file.module}/${file.relative}`, module: file.module, peer });
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
return violations.sort((a, b) => a.file.localeCompare(b.file) || a.peer.localeCompare(b.peer));
|
|
7539
|
+
}
|
|
7540
|
+
function findOptionalPeerViolations(modulesDir) {
|
|
7541
|
+
if (!import_node_fs2.default.existsSync(modulesDir)) return [];
|
|
7542
|
+
const manifests = {};
|
|
7543
|
+
for (const name of import_node_fs2.default.readdirSync(modulesDir)) {
|
|
7544
|
+
const manifestPath = import_node_path2.default.join(modulesDir, name, "module.json");
|
|
7545
|
+
if (!import_node_fs2.default.existsSync(manifestPath)) continue;
|
|
7546
|
+
manifests[name] = JSON.parse(import_node_fs2.default.readFileSync(manifestPath, "utf8"));
|
|
7547
|
+
}
|
|
7548
|
+
const features = Object.keys(manifests).filter((name) => manifests[name].kind === "feature");
|
|
7549
|
+
const peersByModule = {};
|
|
7550
|
+
for (const [name, manifest] of Object.entries(manifests)) {
|
|
7551
|
+
const uses = new Set(manifest.uses ?? []);
|
|
7552
|
+
peersByModule[name] = features.filter((peer) => peer !== name && !uses.has(peer));
|
|
7553
|
+
}
|
|
7554
|
+
return findModuleBoundaryViolations(modulesDir, peersByModule);
|
|
7555
|
+
}
|
|
7556
|
+
function formatBoundaryViolations(modulesDirLabel, violations) {
|
|
7557
|
+
const files = new Set(violations.map((v) => v.file)).size;
|
|
7558
|
+
return `${files} Module file(s) import a peer Module that may be absent (not in the Module's \`uses\`, or excluded by --enabled).
|
|
7559
|
+
\`next build\` type-checks them after the peer is gone. For each line, move the file into ${modulesDirLabel}/<module>/enhancements/<peer>/ or declare <peer> in the module's module.json \`uses\` (and enable it).
|
|
7560
|
+
` + violations.map((v) => `${modulesDirLabel}/${v.file}: ${v.peer}`).join("\n");
|
|
7561
|
+
}
|
|
7562
|
+
|
|
7500
7563
|
// src/index.ts
|
|
7501
7564
|
var MODULES_DIR2 = MODULES_SANDBOX_DIR;
|
|
7502
7565
|
function readComposeInputs(cwd) {
|
|
7503
|
-
const file =
|
|
7504
|
-
if (!
|
|
7566
|
+
const file = import_node_path3.default.join(cwd, COMPOSE_INPUTS_PATH);
|
|
7567
|
+
if (!import_node_fs3.default.existsSync(file)) return { connectedStores: [], installs: {} };
|
|
7505
7568
|
let raw;
|
|
7506
7569
|
try {
|
|
7507
|
-
raw = JSON.parse(
|
|
7570
|
+
raw = JSON.parse(import_node_fs3.default.readFileSync(file, "utf8"));
|
|
7508
7571
|
} catch (error) {
|
|
7509
7572
|
throw new Error(
|
|
7510
7573
|
`[compose] ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -7546,18 +7609,18 @@ function stubGitignoreEntries(artifactPaths) {
|
|
|
7546
7609
|
function writeStubGitignore(cwd, artifactPaths) {
|
|
7547
7610
|
const entries = stubGitignoreEntries(artifactPaths);
|
|
7548
7611
|
const content = [STUB_GITIGNORE_HEADER, ...entries].join("\n") + "\n";
|
|
7549
|
-
const file =
|
|
7550
|
-
const existing =
|
|
7612
|
+
const file = import_node_path3.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7613
|
+
const existing = import_node_fs3.default.existsSync(file) ? import_node_fs3.default.readFileSync(file, "utf8") : null;
|
|
7551
7614
|
if (existing !== content) {
|
|
7552
|
-
|
|
7553
|
-
|
|
7615
|
+
import_node_fs3.default.mkdirSync(import_node_path3.default.dirname(file), { recursive: true });
|
|
7616
|
+
import_node_fs3.default.writeFileSync(file, content);
|
|
7554
7617
|
}
|
|
7555
7618
|
return entries.length;
|
|
7556
7619
|
}
|
|
7557
7620
|
function assertStubGitignoreOwned(cwd) {
|
|
7558
|
-
const file =
|
|
7559
|
-
if (!
|
|
7560
|
-
const firstLine =
|
|
7621
|
+
const file = import_node_path3.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
7622
|
+
if (!import_node_fs3.default.existsSync(file)) return;
|
|
7623
|
+
const firstLine = import_node_fs3.default.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
|
|
7561
7624
|
if (firstLine !== STUB_GITIGNORE_HEADER) {
|
|
7562
7625
|
throw new Error(
|
|
7563
7626
|
`[compose] refusing to overwrite ${STUB_GITIGNORE_PATH}: it is not compose-generated (line 1 is not the generated header)`
|
|
@@ -7565,8 +7628,8 @@ function assertStubGitignoreOwned(cwd) {
|
|
|
7565
7628
|
}
|
|
7566
7629
|
}
|
|
7567
7630
|
function assertAppRoot(cwd) {
|
|
7568
|
-
const appPath =
|
|
7569
|
-
if (!
|
|
7631
|
+
const appPath = import_node_path3.default.join(cwd, APP_PACKAGE_JSON);
|
|
7632
|
+
if (!import_node_fs3.default.existsSync(appPath)) {
|
|
7570
7633
|
throw new Error(
|
|
7571
7634
|
`[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
|
|
7572
7635
|
);
|
|
@@ -7612,9 +7675,9 @@ ${porcelain.trim()}`
|
|
|
7612
7675
|
}
|
|
7613
7676
|
var APP_SRC_DIR = "apps/web/src";
|
|
7614
7677
|
function walkSources(dir, skip, out) {
|
|
7615
|
-
if (!
|
|
7616
|
-
for (const entry of
|
|
7617
|
-
const full =
|
|
7678
|
+
if (!import_node_fs3.default.existsSync(dir)) return;
|
|
7679
|
+
for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
|
|
7680
|
+
const full = import_node_path3.default.join(dir, entry.name);
|
|
7618
7681
|
if (full === skip) continue;
|
|
7619
7682
|
if (entry.isDirectory()) walkSources(full, skip, out);
|
|
7620
7683
|
else if (/\.[cm]?[jt]sx?$/.test(entry.name)) out.push(full);
|
|
@@ -7623,13 +7686,13 @@ function walkSources(dir, skip, out) {
|
|
|
7623
7686
|
function assertNoAppLayerImporters(cwd, excluded) {
|
|
7624
7687
|
if (excluded.length === 0) return;
|
|
7625
7688
|
const files = [];
|
|
7626
|
-
walkSources(
|
|
7689
|
+
walkSources(import_node_path3.default.join(cwd, APP_SRC_DIR), import_node_path3.default.join(cwd, MODULES_DIR2), files);
|
|
7627
7690
|
const offenders = [];
|
|
7628
7691
|
for (const file of files) {
|
|
7629
|
-
const source =
|
|
7692
|
+
const source = import_node_fs3.default.readFileSync(file, "utf8");
|
|
7630
7693
|
for (const name of excluded) {
|
|
7631
7694
|
if (new RegExp(`@/modules/${name}(?=["'\`/])`).test(source)) {
|
|
7632
|
-
offenders.push(`${
|
|
7695
|
+
offenders.push(`${import_node_path3.default.relative(cwd, file)}: ${name}`);
|
|
7633
7696
|
}
|
|
7634
7697
|
}
|
|
7635
7698
|
}
|
|
@@ -7641,9 +7704,44 @@ function assertNoAppLayerImporters(cwd, excluded) {
|
|
|
7641
7704
|
);
|
|
7642
7705
|
}
|
|
7643
7706
|
}
|
|
7707
|
+
var TSCONFIG_PATH = "apps/web/tsconfig.json";
|
|
7708
|
+
var BUILD_TSCONFIG_PATH = "apps/web/tsconfig.build.gen.json";
|
|
7709
|
+
var BUILD_TSCONFIG_HEADER = "// Generated by stardeck-compose \u2014 the tsconfig `next build` type-checks. Do not edit or commit.";
|
|
7710
|
+
function writeBuildTsconfig(cwd, excluded) {
|
|
7711
|
+
const baseFile = import_node_path3.default.join(cwd, TSCONFIG_PATH);
|
|
7712
|
+
if (!import_node_fs3.default.existsSync(import_node_path3.default.join(cwd, MODULES_DIR2)) || !import_node_fs3.default.existsSync(baseFile)) return;
|
|
7713
|
+
const base = import_typescript6.default.readConfigFile(baseFile, import_typescript6.default.sys.readFile);
|
|
7714
|
+
if (base.error) {
|
|
7715
|
+
throw new Error(
|
|
7716
|
+
`[compose] cannot read ${TSCONFIG_PATH}: ${import_typescript6.default.flattenDiagnosticMessageText(base.error.messageText, "\n")}`
|
|
7717
|
+
);
|
|
7718
|
+
}
|
|
7719
|
+
const baseExclude = base.config?.exclude ?? ["node_modules"];
|
|
7720
|
+
const modulesDir = import_node_path3.default.posix.relative("apps/web", MODULES_DIR2);
|
|
7721
|
+
const config = {
|
|
7722
|
+
extends: "./tsconfig.json",
|
|
7723
|
+
exclude: [
|
|
7724
|
+
...baseExclude,
|
|
7725
|
+
...excluded.flatMap((name) => [
|
|
7726
|
+
`${modulesDir}/${name}`,
|
|
7727
|
+
`${modulesDir}/*/enhancements/${name}`
|
|
7728
|
+
])
|
|
7729
|
+
]
|
|
7730
|
+
};
|
|
7731
|
+
const content = `${BUILD_TSCONFIG_HEADER}
|
|
7732
|
+
${JSON.stringify(config, null, 2)}
|
|
7733
|
+
`;
|
|
7734
|
+
const file = import_node_path3.default.join(cwd, BUILD_TSCONFIG_PATH);
|
|
7735
|
+
if (!import_node_fs3.default.existsSync(file) || import_node_fs3.default.readFileSync(file, "utf8") !== content) {
|
|
7736
|
+
import_node_fs3.default.writeFileSync(file, content);
|
|
7737
|
+
}
|
|
7738
|
+
console.log(
|
|
7739
|
+
`[compose] ${BUILD_TSCONFIG_PATH} excludes ${excluded.length} Module(s): ${excluded.join(",") || "none"}`
|
|
7740
|
+
);
|
|
7741
|
+
}
|
|
7644
7742
|
function removeDir(absolute) {
|
|
7645
|
-
if (!
|
|
7646
|
-
|
|
7743
|
+
if (!import_node_fs3.default.existsSync(absolute)) return false;
|
|
7744
|
+
import_node_fs3.default.rmSync(absolute, { recursive: true, force: true });
|
|
7647
7745
|
return true;
|
|
7648
7746
|
}
|
|
7649
7747
|
async function compose(options) {
|
|
@@ -7670,6 +7768,16 @@ async function compose(options) {
|
|
|
7670
7768
|
dryRun: true
|
|
7671
7769
|
});
|
|
7672
7770
|
assertNoAppLayerImporters(options.cwd, preview.excludedModules);
|
|
7771
|
+
const violations = findModuleBoundaryViolations(
|
|
7772
|
+
import_node_path3.default.join(options.cwd, MODULES_DIR2),
|
|
7773
|
+
Object.fromEntries(preview.composedModules.map((name) => [name, preview.excludedModules])),
|
|
7774
|
+
{ pruning: true }
|
|
7775
|
+
);
|
|
7776
|
+
if (violations.length > 0) {
|
|
7777
|
+
throw new Error(
|
|
7778
|
+
`[compose] refusing --prune: ${formatBoundaryViolations(MODULES_DIR2, violations)}`
|
|
7779
|
+
);
|
|
7780
|
+
}
|
|
7673
7781
|
}
|
|
7674
7782
|
const result = await reconcileCompositionArtifacts(context, {
|
|
7675
7783
|
enabledModulesRaw: options.enabled,
|
|
@@ -7678,14 +7786,15 @@ async function compose(options) {
|
|
|
7678
7786
|
skipComposeInputs: true
|
|
7679
7787
|
});
|
|
7680
7788
|
const gitignoreEntries = options.enabled ? null : writeStubGitignore(options.cwd, result.artifactPaths);
|
|
7789
|
+
writeBuildTsconfig(options.cwd, result.excludedModules);
|
|
7681
7790
|
let pruned = 0;
|
|
7682
7791
|
if (options.prune) {
|
|
7683
7792
|
for (const name of result.excludedModules) {
|
|
7684
|
-
if (removeDir(
|
|
7793
|
+
if (removeDir(import_node_path3.default.join(options.cwd, MODULES_DIR2, name))) pruned++;
|
|
7685
7794
|
}
|
|
7686
7795
|
for (const owner of result.composedModules) {
|
|
7687
7796
|
for (const peer of result.excludedModules) {
|
|
7688
|
-
if (removeDir(
|
|
7797
|
+
if (removeDir(import_node_path3.default.join(options.cwd, MODULES_DIR2, owner, "enhancements", peer))) pruned++;
|
|
7689
7798
|
}
|
|
7690
7799
|
}
|
|
7691
7800
|
}
|
|
@@ -7701,15 +7810,28 @@ async function compose(options) {
|
|
|
7701
7810
|
|
|
7702
7811
|
// src/cli.ts
|
|
7703
7812
|
async function main() {
|
|
7704
|
-
assertSupportedTypescript(
|
|
7813
|
+
assertSupportedTypescript(import_typescript7.default.versionMajorMinor);
|
|
7705
7814
|
const { values } = (0, import_node_util.parseArgs)({
|
|
7706
7815
|
options: {
|
|
7707
7816
|
enabled: { type: "string" },
|
|
7708
7817
|
prune: { type: "boolean", default: false },
|
|
7709
|
-
cwd: { type: "string" }
|
|
7818
|
+
cwd: { type: "string" },
|
|
7819
|
+
"check-boundaries": { type: "boolean", default: false }
|
|
7710
7820
|
},
|
|
7711
7821
|
strict: true
|
|
7712
7822
|
});
|
|
7823
|
+
if (values["check-boundaries"]) {
|
|
7824
|
+
const violations = findOptionalPeerViolations(
|
|
7825
|
+
import_node_path4.default.join(values.cwd ?? process.cwd(), MODULES_SANDBOX_DIR)
|
|
7826
|
+
);
|
|
7827
|
+
if (violations.length > 0) {
|
|
7828
|
+
throw new Error(
|
|
7829
|
+
`[compose] --check-boundaries: ${formatBoundaryViolations(MODULES_SANDBOX_DIR, violations)}`
|
|
7830
|
+
);
|
|
7831
|
+
}
|
|
7832
|
+
console.log("[compose] --check-boundaries: no Module file reaches a peer outside its `uses`");
|
|
7833
|
+
return;
|
|
7834
|
+
}
|
|
7713
7835
|
const enabled = values.enabled ?? process.env[ENABLED_MODULES_ENV];
|
|
7714
7836
|
if (values.prune && !enabled) {
|
|
7715
7837
|
throw new Error(
|