@stardeck-customer-apps/compose 0.1.0 → 0.3.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 +42 -11
- package/dist/cli.js +302 -138
- package/dist/index.d.mts +67 -1
- package/dist/index.d.ts +67 -1
- package/dist/index.js +307 -139
- package/dist/index.mjs +302 -138
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -30,7 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
|
|
33
|
+
COMPOSE_INPUTS_PATH: () => COMPOSE_INPUTS_PATH,
|
|
34
|
+
assertSupportedTypescript: () => assertSupportedTypescript,
|
|
35
|
+
compose: () => compose,
|
|
36
|
+
escapeGitignore: () => escapeGitignore,
|
|
37
|
+
readComposeInputs: () => readComposeInputs
|
|
34
38
|
});
|
|
35
39
|
module.exports = __toCommonJS(index_exports);
|
|
36
40
|
var import_node_child_process2 = require("child_process");
|
|
@@ -2063,6 +2067,29 @@ var initialModuleCompositionSchema = import_zod6.z.object({
|
|
|
2063
2067
|
});
|
|
2064
2068
|
var MODULE_SETUP_ATTEMPT_STALE_MS = 10 * 60 * 1e3;
|
|
2065
2069
|
|
|
2070
|
+
// ../../packages/lib/src/shared/compose-inputs.ts
|
|
2071
|
+
var import_zod7 = require("zod");
|
|
2072
|
+
var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
2073
|
+
var composeInputsSchema = import_zod7.z.strictObject({
|
|
2074
|
+
connectedStores: import_zod7.z.array(
|
|
2075
|
+
import_zod7.z.strictObject({
|
|
2076
|
+
storeId: import_zod7.z.string().min(1),
|
|
2077
|
+
slug: import_zod7.z.string().min(1),
|
|
2078
|
+
// A row with an empty binding key must not fail a deploy: the rail
|
|
2079
|
+
// treats falsy as "no binding key", so normalise rather than reject.
|
|
2080
|
+
bindingKey: import_zod7.z.string().nullish().transform((value) => value || null),
|
|
2081
|
+
accessLevel: import_zod7.z.enum(["read", "write", "admin"])
|
|
2082
|
+
})
|
|
2083
|
+
).default([]),
|
|
2084
|
+
installs: import_zod7.z.record(
|
|
2085
|
+
import_zod7.z.string(),
|
|
2086
|
+
import_zod7.z.strictObject({
|
|
2087
|
+
installedVersion: import_zod7.z.string().min(1),
|
|
2088
|
+
datastoreBindings: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).nullish()
|
|
2089
|
+
})
|
|
2090
|
+
).default({})
|
|
2091
|
+
});
|
|
2092
|
+
|
|
2066
2093
|
// ../../packages/lib/src/shared/module-enablement.ts
|
|
2067
2094
|
function isModuleEnabledByList(raw, name, kindOf) {
|
|
2068
2095
|
if (!raw) return true;
|
|
@@ -3554,6 +3581,7 @@ function planCompositionArtifacts(input) {
|
|
|
3554
3581
|
endpointStubs: stubs.endpointStubs,
|
|
3555
3582
|
routeStubs: stubs.routeStubs,
|
|
3556
3583
|
writes,
|
|
3584
|
+
desiredPaths: [...desired.keys()].sort(compareNames2),
|
|
3557
3585
|
deletes
|
|
3558
3586
|
}
|
|
3559
3587
|
};
|
|
@@ -3563,6 +3591,27 @@ function dirnamePosix(filePath) {
|
|
|
3563
3591
|
return idx <= 0 ? "." : filePath.slice(0, idx);
|
|
3564
3592
|
}
|
|
3565
3593
|
|
|
3594
|
+
// ../../packages/lib/src/server/module-rail/compose-inputs.ts
|
|
3595
|
+
async function buildComposeInputs(input) {
|
|
3596
|
+
const { db, projectId, deps } = input;
|
|
3597
|
+
const connectedStores = await deps.listDatabaseStoresForProject(db, projectId);
|
|
3598
|
+
const installRows = await Promise.all(
|
|
3599
|
+
input.moduleNames.map(async (moduleName) => ({
|
|
3600
|
+
moduleName,
|
|
3601
|
+
install: await deps.getModuleInstall(db, projectId, moduleName)
|
|
3602
|
+
}))
|
|
3603
|
+
);
|
|
3604
|
+
const installs = {};
|
|
3605
|
+
for (const { moduleName, install } of installRows) {
|
|
3606
|
+
if (!install) continue;
|
|
3607
|
+
installs[moduleName] = {
|
|
3608
|
+
installedVersion: install.installedVersion,
|
|
3609
|
+
datastoreBindings: install.datastoreBindings ?? null
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
return composeInputsSchema.parse({ connectedStores, installs });
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3566
3615
|
// ../../packages/lib/src/shared/data-store-manifest.ts
|
|
3567
3616
|
function dataStoreSlug(name) {
|
|
3568
3617
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -3759,162 +3808,162 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
|
|
|
3759
3808
|
}
|
|
3760
3809
|
|
|
3761
3810
|
// ../../packages/lib/src/shared/schema-ops.ts
|
|
3762
|
-
var
|
|
3763
|
-
var columnSpecSchema =
|
|
3764
|
-
name:
|
|
3765
|
-
pgType:
|
|
3766
|
-
nullable:
|
|
3811
|
+
var import_zod8 = require("zod");
|
|
3812
|
+
var columnSpecSchema = import_zod8.z.object({
|
|
3813
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3814
|
+
pgType: import_zod8.z.string().min(1),
|
|
3815
|
+
nullable: import_zod8.z.boolean(),
|
|
3767
3816
|
/**
|
|
3768
3817
|
* A SQL expression used as the column default, as a raw string (e.g. `'t'`,
|
|
3769
3818
|
* `0`, `now()`, `gen_random_uuid()`). `null` or omitted means no default.
|
|
3770
3819
|
* NOT a JS literal — must already be a valid SQL expression.
|
|
3771
3820
|
*/
|
|
3772
|
-
default:
|
|
3773
|
-
fieldType:
|
|
3774
|
-
fieldConfig:
|
|
3821
|
+
default: import_zod8.z.string().nullable().optional(),
|
|
3822
|
+
fieldType: import_zod8.z.string().optional(),
|
|
3823
|
+
fieldConfig: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.unknown()).optional()
|
|
3775
3824
|
});
|
|
3776
|
-
var referentialActionSchema =
|
|
3825
|
+
var referentialActionSchema = import_zod8.z.enum([
|
|
3777
3826
|
"cascade",
|
|
3778
3827
|
"set null",
|
|
3779
3828
|
"set default",
|
|
3780
3829
|
"restrict",
|
|
3781
3830
|
"no action"
|
|
3782
3831
|
]);
|
|
3783
|
-
var schemaOpSchema =
|
|
3832
|
+
var schemaOpSchema = import_zod8.z.discriminatedUnion("type", [
|
|
3784
3833
|
// Tables
|
|
3785
|
-
|
|
3786
|
-
type:
|
|
3787
|
-
table:
|
|
3788
|
-
columns:
|
|
3789
|
-
primaryKey:
|
|
3790
|
-
ifNotExists:
|
|
3834
|
+
import_zod8.z.object({
|
|
3835
|
+
type: import_zod8.z.literal("createTable"),
|
|
3836
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3837
|
+
columns: import_zod8.z.array(columnSpecSchema).min(1),
|
|
3838
|
+
primaryKey: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
3839
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3791
3840
|
}),
|
|
3792
|
-
|
|
3793
|
-
type:
|
|
3794
|
-
table:
|
|
3795
|
-
cascade:
|
|
3796
|
-
ifExists:
|
|
3841
|
+
import_zod8.z.object({
|
|
3842
|
+
type: import_zod8.z.literal("dropTable"),
|
|
3843
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3844
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3845
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3797
3846
|
}),
|
|
3798
|
-
|
|
3799
|
-
type:
|
|
3800
|
-
from:
|
|
3801
|
-
to:
|
|
3847
|
+
import_zod8.z.object({
|
|
3848
|
+
type: import_zod8.z.literal("renameTable"),
|
|
3849
|
+
from: import_zod8.z.string().min(1).max(63),
|
|
3850
|
+
to: import_zod8.z.string().min(1).max(63)
|
|
3802
3851
|
}),
|
|
3803
3852
|
// Columns
|
|
3804
|
-
|
|
3805
|
-
type:
|
|
3806
|
-
table:
|
|
3853
|
+
import_zod8.z.object({
|
|
3854
|
+
type: import_zod8.z.literal("addColumn"),
|
|
3855
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3807
3856
|
column: columnSpecSchema,
|
|
3808
|
-
ifNotExists:
|
|
3857
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3809
3858
|
}),
|
|
3810
|
-
|
|
3811
|
-
type:
|
|
3812
|
-
table:
|
|
3813
|
-
column:
|
|
3814
|
-
cascade:
|
|
3815
|
-
ifExists:
|
|
3859
|
+
import_zod8.z.object({
|
|
3860
|
+
type: import_zod8.z.literal("dropColumn"),
|
|
3861
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3862
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3863
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3864
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3816
3865
|
}),
|
|
3817
|
-
|
|
3818
|
-
type:
|
|
3819
|
-
table:
|
|
3820
|
-
from:
|
|
3821
|
-
to:
|
|
3866
|
+
import_zod8.z.object({
|
|
3867
|
+
type: import_zod8.z.literal("renameColumn"),
|
|
3868
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3869
|
+
from: import_zod8.z.string().min(1).max(63),
|
|
3870
|
+
to: import_zod8.z.string().min(1).max(63)
|
|
3822
3871
|
}),
|
|
3823
|
-
|
|
3824
|
-
type:
|
|
3825
|
-
table:
|
|
3826
|
-
column:
|
|
3827
|
-
newType:
|
|
3872
|
+
import_zod8.z.object({
|
|
3873
|
+
type: import_zod8.z.literal("alterColumnType"),
|
|
3874
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3875
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3876
|
+
newType: import_zod8.z.string().min(1),
|
|
3828
3877
|
/** Optional USING expression for potentially-lossy casts */
|
|
3829
|
-
using:
|
|
3878
|
+
using: import_zod8.z.string().optional()
|
|
3830
3879
|
}),
|
|
3831
|
-
|
|
3832
|
-
type:
|
|
3833
|
-
table:
|
|
3834
|
-
column:
|
|
3835
|
-
nullable:
|
|
3880
|
+
import_zod8.z.object({
|
|
3881
|
+
type: import_zod8.z.literal("alterColumnNullable"),
|
|
3882
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3883
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3884
|
+
nullable: import_zod8.z.boolean()
|
|
3836
3885
|
}),
|
|
3837
|
-
|
|
3838
|
-
type:
|
|
3839
|
-
table:
|
|
3840
|
-
column:
|
|
3886
|
+
import_zod8.z.object({
|
|
3887
|
+
type: import_zod8.z.literal("alterColumnDefault"),
|
|
3888
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3889
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3841
3890
|
/** null means drop the default; otherwise a SQL expression string. */
|
|
3842
|
-
default:
|
|
3891
|
+
default: import_zod8.z.string().nullable()
|
|
3843
3892
|
}),
|
|
3844
3893
|
// Indexes
|
|
3845
|
-
|
|
3846
|
-
type:
|
|
3847
|
-
table:
|
|
3848
|
-
name:
|
|
3849
|
-
columns:
|
|
3850
|
-
unique:
|
|
3894
|
+
import_zod8.z.object({
|
|
3895
|
+
type: import_zod8.z.literal("addIndex"),
|
|
3896
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3897
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3898
|
+
columns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1),
|
|
3899
|
+
unique: import_zod8.z.boolean().optional(),
|
|
3851
3900
|
/** Partial-index WHERE clause, as a raw SQL expression */
|
|
3852
|
-
where:
|
|
3853
|
-
ifNotExists:
|
|
3901
|
+
where: import_zod8.z.string().optional(),
|
|
3902
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3854
3903
|
}),
|
|
3855
|
-
|
|
3856
|
-
type:
|
|
3857
|
-
name:
|
|
3858
|
-
cascade:
|
|
3859
|
-
ifExists:
|
|
3904
|
+
import_zod8.z.object({
|
|
3905
|
+
type: import_zod8.z.literal("dropIndex"),
|
|
3906
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3907
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3908
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3860
3909
|
}),
|
|
3861
3910
|
// Constraints
|
|
3862
|
-
|
|
3863
|
-
type:
|
|
3864
|
-
table:
|
|
3865
|
-
name:
|
|
3866
|
-
columns:
|
|
3867
|
-
refTable:
|
|
3868
|
-
refColumns:
|
|
3911
|
+
import_zod8.z.object({
|
|
3912
|
+
type: import_zod8.z.literal("addForeignKey"),
|
|
3913
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3914
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3915
|
+
columns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1),
|
|
3916
|
+
refTable: import_zod8.z.string().min(1).max(63),
|
|
3917
|
+
refColumns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1),
|
|
3869
3918
|
onDelete: referentialActionSchema.optional(),
|
|
3870
3919
|
onUpdate: referentialActionSchema.optional()
|
|
3871
3920
|
}),
|
|
3872
|
-
|
|
3873
|
-
type:
|
|
3874
|
-
table:
|
|
3875
|
-
name:
|
|
3876
|
-
columns:
|
|
3921
|
+
import_zod8.z.object({
|
|
3922
|
+
type: import_zod8.z.literal("addUniqueConstraint"),
|
|
3923
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3924
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3925
|
+
columns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1)
|
|
3877
3926
|
}),
|
|
3878
|
-
|
|
3879
|
-
type:
|
|
3880
|
-
table:
|
|
3881
|
-
name:
|
|
3882
|
-
expression:
|
|
3927
|
+
import_zod8.z.object({
|
|
3928
|
+
type: import_zod8.z.literal("addCheckConstraint"),
|
|
3929
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3930
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3931
|
+
expression: import_zod8.z.string().min(1)
|
|
3883
3932
|
}),
|
|
3884
|
-
|
|
3885
|
-
type:
|
|
3886
|
-
table:
|
|
3887
|
-
name:
|
|
3888
|
-
cascade:
|
|
3889
|
-
ifExists:
|
|
3933
|
+
import_zod8.z.object({
|
|
3934
|
+
type: import_zod8.z.literal("dropConstraint"),
|
|
3935
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3936
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3937
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3938
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3890
3939
|
}),
|
|
3891
3940
|
// Enum types
|
|
3892
|
-
|
|
3893
|
-
type:
|
|
3894
|
-
name:
|
|
3895
|
-
values:
|
|
3941
|
+
import_zod8.z.object({
|
|
3942
|
+
type: import_zod8.z.literal("createEnum"),
|
|
3943
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3944
|
+
values: import_zod8.z.array(import_zod8.z.string().min(1)).min(1)
|
|
3896
3945
|
}),
|
|
3897
|
-
|
|
3898
|
-
type:
|
|
3899
|
-
name:
|
|
3900
|
-
value:
|
|
3946
|
+
import_zod8.z.object({
|
|
3947
|
+
type: import_zod8.z.literal("alterEnumAddValue"),
|
|
3948
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3949
|
+
value: import_zod8.z.string().min(1),
|
|
3901
3950
|
/** Position the new value before an existing one (mutually exclusive with `after`) */
|
|
3902
|
-
before:
|
|
3951
|
+
before: import_zod8.z.string().optional(),
|
|
3903
3952
|
/** Position the new value after an existing one (mutually exclusive with `before`) */
|
|
3904
|
-
after:
|
|
3905
|
-
ifNotExists:
|
|
3953
|
+
after: import_zod8.z.string().optional(),
|
|
3954
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3906
3955
|
}),
|
|
3907
|
-
|
|
3908
|
-
type:
|
|
3909
|
-
name:
|
|
3910
|
-
cascade:
|
|
3911
|
-
ifExists:
|
|
3956
|
+
import_zod8.z.object({
|
|
3957
|
+
type: import_zod8.z.literal("dropEnum"),
|
|
3958
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3959
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3960
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3912
3961
|
}),
|
|
3913
3962
|
// Extensions
|
|
3914
|
-
|
|
3915
|
-
type:
|
|
3916
|
-
name:
|
|
3917
|
-
ifNotExists:
|
|
3963
|
+
import_zod8.z.object({
|
|
3964
|
+
type: import_zod8.z.literal("createExtension"),
|
|
3965
|
+
name: import_zod8.z.string().min(1),
|
|
3966
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3918
3967
|
}),
|
|
3919
3968
|
// Data operations
|
|
3920
3969
|
/**
|
|
@@ -3922,26 +3971,26 @@ var schemaOpSchema = import_zod7.z.discriminatedUnion("type", [
|
|
|
3922
3971
|
* destructive — requires explicit confirm to replay on prod. The
|
|
3923
3972
|
* `description` is what the agent/user sees at confirm time.
|
|
3924
3973
|
*/
|
|
3925
|
-
|
|
3926
|
-
type:
|
|
3927
|
-
description:
|
|
3928
|
-
sql:
|
|
3974
|
+
import_zod8.z.object({
|
|
3975
|
+
type: import_zod8.z.literal("backfill"),
|
|
3976
|
+
description: import_zod8.z.string().min(1),
|
|
3977
|
+
sql: import_zod8.z.string().min(1)
|
|
3929
3978
|
}),
|
|
3930
3979
|
/**
|
|
3931
3980
|
* Escape hatch for DDL the structured ops can't express. `destructive`
|
|
3932
3981
|
* forces classification — use false only for genuinely additive-only SQL
|
|
3933
3982
|
* (e.g. CREATE EXTENSION variants the structured op doesn't cover).
|
|
3934
3983
|
*/
|
|
3935
|
-
|
|
3936
|
-
type:
|
|
3937
|
-
description:
|
|
3938
|
-
sql:
|
|
3939
|
-
destructive:
|
|
3984
|
+
import_zod8.z.object({
|
|
3985
|
+
type: import_zod8.z.literal("rawSql"),
|
|
3986
|
+
description: import_zod8.z.string().min(1),
|
|
3987
|
+
sql: import_zod8.z.string().min(1),
|
|
3988
|
+
destructive: import_zod8.z.boolean()
|
|
3940
3989
|
})
|
|
3941
3990
|
]);
|
|
3942
3991
|
|
|
3943
3992
|
// ../../packages/lib/src/server/module-rail/ops.ts
|
|
3944
|
-
var
|
|
3993
|
+
var import_zod9 = require("zod");
|
|
3945
3994
|
|
|
3946
3995
|
// ../../packages/lib/src/server/module-rail/reconcile.ts
|
|
3947
3996
|
var SANDBOX_BATCH_SIZE = 200;
|
|
@@ -3982,8 +4031,8 @@ function countedCompositionContext(context) {
|
|
|
3982
4031
|
}
|
|
3983
4032
|
};
|
|
3984
4033
|
}
|
|
3985
|
-
async function listPresentModuleNames(
|
|
3986
|
-
const entries = await listImmediateEntries(
|
|
4034
|
+
async function listPresentModuleNames(sandbox) {
|
|
4035
|
+
const entries = await listImmediateEntries(sandbox, MODULES_SANDBOX_DIR, {
|
|
3987
4036
|
optional: true,
|
|
3988
4037
|
label: "modules"
|
|
3989
4038
|
});
|
|
@@ -4187,24 +4236,24 @@ async function discoverModuleRouteEntries(context, moduleName) {
|
|
|
4187
4236
|
}
|
|
4188
4237
|
return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
|
|
4189
4238
|
}
|
|
4190
|
-
async function writeSandboxFileAtomic(
|
|
4239
|
+
async function writeSandboxFileAtomic(target, path3, content) {
|
|
4191
4240
|
const dir = dirnamePosix(path3);
|
|
4192
|
-
const tmp = `${path3}.tmp.${
|
|
4241
|
+
const tmp = `${path3}.tmp.${target.runId}`;
|
|
4193
4242
|
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
4194
4243
|
const cmd = [
|
|
4195
4244
|
`mkdir -p ${shellQuote(dir)}`,
|
|
4196
4245
|
`printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
|
|
4197
4246
|
`mv -f ${shellQuote(tmp)} ${shellQuote(path3)}`
|
|
4198
4247
|
].join(" && ");
|
|
4199
|
-
const result = await
|
|
4248
|
+
const result = await target.sandbox.exec(cmd, { raiseOnError: false });
|
|
4200
4249
|
if (result.exitCode !== 0) {
|
|
4201
|
-
const cleanup = await
|
|
4250
|
+
const cleanup = await target.sandbox.exec(`rm -f ${shellQuote(tmp)}`, {
|
|
4202
4251
|
raiseOnError: false
|
|
4203
4252
|
});
|
|
4204
4253
|
if (cleanup.exitCode !== 0) {
|
|
4205
4254
|
const cleanupDetail = typeof cleanup.error === "string" && cleanup.error.trim() || cleanup.output.trim() || `exit ${cleanup.exitCode}`;
|
|
4206
4255
|
console.log(
|
|
4207
|
-
`[ModuleRail] runId=${
|
|
4256
|
+
`[ModuleRail] runId=${target.runId} composition artifacts failed to clean temp ${redactSecrets(tmp)}: ${redactSecrets(cleanupDetail)}`
|
|
4208
4257
|
);
|
|
4209
4258
|
}
|
|
4210
4259
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
@@ -4262,6 +4311,40 @@ async function deleteSandboxFile(context, path3) {
|
|
|
4262
4311
|
);
|
|
4263
4312
|
}
|
|
4264
4313
|
}
|
|
4314
|
+
async function writeComposeInputs(target) {
|
|
4315
|
+
const ignored = await target.sandbox.exec(
|
|
4316
|
+
`git check-ignore -q ${shellQuote(COMPOSE_INPUTS_PATH)}`,
|
|
4317
|
+
{ raiseOnError: false }
|
|
4318
|
+
);
|
|
4319
|
+
if (ignored.exitCode === 1) {
|
|
4320
|
+
console.log(
|
|
4321
|
+
`[ComposeInputs] project=${target.projectId} skipped: ${COMPOSE_INPUTS_PATH} is not ignored in this checkout`
|
|
4322
|
+
);
|
|
4323
|
+
return false;
|
|
4324
|
+
}
|
|
4325
|
+
if (ignored.exitCode !== 0) {
|
|
4326
|
+
const detail = typeof ignored.error === "string" && ignored.error.trim() || ignored.output.trim() || "(no output)";
|
|
4327
|
+
throw new Error(
|
|
4328
|
+
`[ComposeInputs] git check-ignore failed in this checkout (exit ${ignored.exitCode}): ${redactSecrets(detail)}`
|
|
4329
|
+
);
|
|
4330
|
+
}
|
|
4331
|
+
const moduleNames = await listPresentModuleNames(target.sandbox);
|
|
4332
|
+
const inputs = await buildComposeInputs({
|
|
4333
|
+
db: target.db,
|
|
4334
|
+
projectId: target.projectId,
|
|
4335
|
+
deps: target.deps,
|
|
4336
|
+
moduleNames
|
|
4337
|
+
});
|
|
4338
|
+
const content = `${JSON.stringify(inputs, null, 2)}
|
|
4339
|
+
`;
|
|
4340
|
+
const existing = await target.sandbox.fileExists(COMPOSE_INPUTS_PATH) ? await target.sandbox.readFile(COMPOSE_INPUTS_PATH) : null;
|
|
4341
|
+
const written = existing === null || existing.trimEnd() !== content.trimEnd();
|
|
4342
|
+
if (written) await writeSandboxFileAtomic(target, COMPOSE_INPUTS_PATH, content);
|
|
4343
|
+
console.log(
|
|
4344
|
+
`[ComposeInputs] project=${target.projectId} stores=${inputs.connectedStores.length} installs=${Object.keys(inputs.installs).length} written=${written}`
|
|
4345
|
+
);
|
|
4346
|
+
return written;
|
|
4347
|
+
}
|
|
4265
4348
|
async function applyCompositionArtifactMutations(context, plan, existingFiles) {
|
|
4266
4349
|
const appliedWrites = [];
|
|
4267
4350
|
const appliedDeletes = [];
|
|
@@ -4897,7 +4980,7 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
4897
4980
|
context = counted.context;
|
|
4898
4981
|
const deps = context.deps;
|
|
4899
4982
|
if (!deps) throw new Error("Module rail dependencies are required for composition artifacts");
|
|
4900
|
-
const presentRaw = await listPresentModuleNames(context);
|
|
4983
|
+
const presentRaw = await listPresentModuleNames(context.sandbox);
|
|
4901
4984
|
const presentNames = presentRaw.filter((name) => isValidModuleName(name)).sort((a, b) => a.localeCompare(b));
|
|
4902
4985
|
const moduleFacts = [];
|
|
4903
4986
|
const manifestsByName = {};
|
|
@@ -5117,6 +5200,15 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
5117
5200
|
console.log(
|
|
5118
5201
|
`[ModuleRail] runId=${context.runId} composition artifacts writes=${planned.plan.writes.length} deletes=${planned.plan.deletes.length} modules=${registry.modules.length} excluded=${excludedModules.length} changed=${changed} dryRun=${options.dryRun === true} sandboxExecs=${counted.counts.sandboxExecs} sandboxReads=${counted.counts.sandboxReads}`
|
|
5119
5202
|
);
|
|
5203
|
+
if (!options.dryRun && !options.skipComposeInputs) {
|
|
5204
|
+
await writeComposeInputs({
|
|
5205
|
+
db: context.db,
|
|
5206
|
+
projectId: context.projectId,
|
|
5207
|
+
runId: context.runId,
|
|
5208
|
+
sandbox: context.sandbox,
|
|
5209
|
+
deps
|
|
5210
|
+
});
|
|
5211
|
+
}
|
|
5120
5212
|
if (changed && !options.dryRun) {
|
|
5121
5213
|
await applyCompositionArtifactMutations(context, planned.plan, existingFiles);
|
|
5122
5214
|
}
|
|
@@ -5124,6 +5216,7 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
5124
5216
|
registryPath: planned.plan.registryPath,
|
|
5125
5217
|
writePaths: planned.plan.writes.map((w) => w.path),
|
|
5126
5218
|
deletePaths: planned.plan.deletes,
|
|
5219
|
+
artifactPaths: planned.plan.desiredPaths,
|
|
5127
5220
|
changed,
|
|
5128
5221
|
composedModules: composedNames,
|
|
5129
5222
|
excludedModules
|
|
@@ -5134,11 +5227,75 @@ var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
|
|
|
5134
5227
|
|
|
5135
5228
|
// src/index.ts
|
|
5136
5229
|
var MODULES_DIR2 = MODULES_SANDBOX_DIR;
|
|
5230
|
+
function readComposeInputs(cwd) {
|
|
5231
|
+
const file = import_node_path2.default.join(cwd, COMPOSE_INPUTS_PATH);
|
|
5232
|
+
if (!import_node_fs2.default.existsSync(file)) return { connectedStores: [], installs: {} };
|
|
5233
|
+
let raw;
|
|
5234
|
+
try {
|
|
5235
|
+
raw = JSON.parse(import_node_fs2.default.readFileSync(file, "utf8"));
|
|
5236
|
+
} catch (error) {
|
|
5237
|
+
throw new Error(
|
|
5238
|
+
`[compose] ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
5239
|
+
);
|
|
5240
|
+
}
|
|
5241
|
+
const parsed = composeInputsSchema.safeParse(raw);
|
|
5242
|
+
if (!parsed.success) {
|
|
5243
|
+
const issues = parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
|
|
5244
|
+
throw new Error(`[compose] ${file} is invalid: ${issues}`);
|
|
5245
|
+
}
|
|
5246
|
+
return {
|
|
5247
|
+
connectedStores: parsed.data.connectedStores.map((store) => ({
|
|
5248
|
+
storeId: store.storeId,
|
|
5249
|
+
slug: store.slug,
|
|
5250
|
+
bindingKey: store.bindingKey ?? null,
|
|
5251
|
+
accessLevel: store.accessLevel
|
|
5252
|
+
})),
|
|
5253
|
+
installs: parsed.data.installs
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5256
|
+
function assertSupportedTypescript(versionMajorMinor) {
|
|
5257
|
+
const major = Number.parseInt(versionMajorMinor.split(".")[0] ?? "", 10);
|
|
5258
|
+
if (!Number.isInteger(major) || major < 5 || major >= 7) {
|
|
5259
|
+
throw new Error(`[compose] typescript ${versionMajorMinor} is not supported (need >=5 <7)`);
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5262
|
+
var APP_DIR_PREFIX2 = "apps/web/src/app/";
|
|
5263
|
+
var APP_PACKAGE_JSON = "apps/web/package.json";
|
|
5264
|
+
var STUB_GITIGNORE_PATH = "apps/web/src/app/.gitignore";
|
|
5265
|
+
var STUB_GITIGNORE_HEADER = "# Generated by stardeck-compose \u2014 Module-rail stubs are build outputs; do not edit or commit them.";
|
|
5266
|
+
function escapeGitignore(pattern) {
|
|
5267
|
+
const escaped = pattern.replace(/[\\[\]*?]/g, (char) => `\\${char}`);
|
|
5268
|
+
return /^[#!]/.test(escaped) ? `\\${escaped}` : escaped;
|
|
5269
|
+
}
|
|
5270
|
+
function stubGitignoreEntries(artifactPaths) {
|
|
5271
|
+
return artifactPaths.filter((artifact) => artifact.startsWith(APP_DIR_PREFIX2)).map((artifact) => escapeGitignore(`/${artifact.slice(APP_DIR_PREFIX2.length)}`)).sort();
|
|
5272
|
+
}
|
|
5273
|
+
function writeStubGitignore(cwd, artifactPaths) {
|
|
5274
|
+
const entries = stubGitignoreEntries(artifactPaths);
|
|
5275
|
+
const content = [STUB_GITIGNORE_HEADER, ...entries].join("\n") + "\n";
|
|
5276
|
+
const file = import_node_path2.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
5277
|
+
const existing = import_node_fs2.default.existsSync(file) ? import_node_fs2.default.readFileSync(file, "utf8") : null;
|
|
5278
|
+
if (existing !== content) {
|
|
5279
|
+
import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
|
|
5280
|
+
import_node_fs2.default.writeFileSync(file, content);
|
|
5281
|
+
}
|
|
5282
|
+
return entries.length;
|
|
5283
|
+
}
|
|
5284
|
+
function assertStubGitignoreOwned(cwd) {
|
|
5285
|
+
const file = import_node_path2.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
5286
|
+
if (!import_node_fs2.default.existsSync(file)) return;
|
|
5287
|
+
const firstLine = import_node_fs2.default.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
|
|
5288
|
+
if (firstLine !== STUB_GITIGNORE_HEADER) {
|
|
5289
|
+
throw new Error(
|
|
5290
|
+
`[compose] refusing to overwrite ${STUB_GITIGNORE_PATH}: it is not compose-generated (line 1 is not the generated header)`
|
|
5291
|
+
);
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5137
5294
|
function assertAppRoot(cwd) {
|
|
5138
|
-
const
|
|
5139
|
-
if (!import_node_fs2.default.existsSync(
|
|
5295
|
+
const appPath = import_node_path2.default.join(cwd, APP_PACKAGE_JSON);
|
|
5296
|
+
if (!import_node_fs2.default.existsSync(appPath)) {
|
|
5140
5297
|
throw new Error(
|
|
5141
|
-
`[compose] ${
|
|
5298
|
+
`[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
|
|
5142
5299
|
);
|
|
5143
5300
|
}
|
|
5144
5301
|
}
|
|
@@ -5218,12 +5375,14 @@ function removeDir(absolute) {
|
|
|
5218
5375
|
}
|
|
5219
5376
|
async function compose(options) {
|
|
5220
5377
|
assertAppRoot(options.cwd);
|
|
5378
|
+
assertStubGitignoreOwned(options.cwd);
|
|
5379
|
+
const inputs = readComposeInputs(options.cwd);
|
|
5221
5380
|
if (options.prune) assertModuleTreeClean(options.cwd);
|
|
5222
5381
|
const sandbox = localFsSandbox(options.cwd);
|
|
5223
5382
|
const deps = {
|
|
5224
5383
|
sandbox,
|
|
5225
|
-
getModuleInstall: async () => null,
|
|
5226
|
-
listDatabaseStoresForProject: async () =>
|
|
5384
|
+
getModuleInstall: async (_db, _projectId, moduleName) => inputs.installs[moduleName] ?? null,
|
|
5385
|
+
listDatabaseStoresForProject: async () => inputs.connectedStores
|
|
5227
5386
|
};
|
|
5228
5387
|
const context = {
|
|
5229
5388
|
db: {},
|
|
@@ -5240,8 +5399,12 @@ async function compose(options) {
|
|
|
5240
5399
|
assertNoAppLayerImporters(options.cwd, preview.excludedModules);
|
|
5241
5400
|
}
|
|
5242
5401
|
const result = await reconcileCompositionArtifacts(context, {
|
|
5243
|
-
enabledModulesRaw: options.enabled
|
|
5402
|
+
enabledModulesRaw: options.enabled,
|
|
5403
|
+
// compose is the inputs file's reader: the deps above echo it back, so
|
|
5404
|
+
// letting the rail rewrite it would round-trip a fork's own input.
|
|
5405
|
+
skipComposeInputs: true
|
|
5244
5406
|
});
|
|
5407
|
+
const gitignoreEntries = options.enabled ? null : writeStubGitignore(options.cwd, result.artifactPaths);
|
|
5245
5408
|
let pruned = 0;
|
|
5246
5409
|
if (options.prune) {
|
|
5247
5410
|
for (const name of result.excludedModules) {
|
|
@@ -5258,10 +5421,15 @@ async function compose(options) {
|
|
|
5258
5421
|
excludedModules: result.excludedModules,
|
|
5259
5422
|
writes: result.writePaths.length,
|
|
5260
5423
|
deletes: result.deletePaths.length,
|
|
5261
|
-
pruned
|
|
5424
|
+
pruned,
|
|
5425
|
+
gitignoreEntries
|
|
5262
5426
|
};
|
|
5263
5427
|
}
|
|
5264
5428
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5265
5429
|
0 && (module.exports = {
|
|
5266
|
-
|
|
5430
|
+
COMPOSE_INPUTS_PATH,
|
|
5431
|
+
assertSupportedTypescript,
|
|
5432
|
+
compose,
|
|
5433
|
+
escapeGitignore,
|
|
5434
|
+
readComposeInputs
|
|
5267
5435
|
});
|