@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/cli.js
CHANGED
|
@@ -1886,6 +1886,9 @@ var MODULE_INIT_SERVER_GEN_PATH = `${APP_WEB_PREFIX}src/module-init.server.gen.t
|
|
|
1886
1886
|
var MODULES_GEN_PATH = `${APP_WEB_PREFIX}src/modules.gen.ts`;
|
|
1887
1887
|
var APP_EXTENSIONS_PATH = `${APP_WEB_PREFIX}src/lib/modules/app-extensions.ts`;
|
|
1888
1888
|
|
|
1889
|
+
// src/cli.ts
|
|
1890
|
+
var import_typescript5 = __toESM(require("typescript"));
|
|
1891
|
+
|
|
1889
1892
|
// src/index.ts
|
|
1890
1893
|
var import_node_child_process2 = require("child_process");
|
|
1891
1894
|
var import_node_fs2 = __toESM(require("fs"));
|
|
@@ -2066,6 +2069,29 @@ var initialModuleCompositionSchema = import_zod6.z.object({
|
|
|
2066
2069
|
});
|
|
2067
2070
|
var MODULE_SETUP_ATTEMPT_STALE_MS = 10 * 60 * 1e3;
|
|
2068
2071
|
|
|
2072
|
+
// ../../packages/lib/src/shared/compose-inputs.ts
|
|
2073
|
+
var import_zod7 = require("zod");
|
|
2074
|
+
var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
2075
|
+
var composeInputsSchema = import_zod7.z.strictObject({
|
|
2076
|
+
connectedStores: import_zod7.z.array(
|
|
2077
|
+
import_zod7.z.strictObject({
|
|
2078
|
+
storeId: import_zod7.z.string().min(1),
|
|
2079
|
+
slug: import_zod7.z.string().min(1),
|
|
2080
|
+
// A row with an empty binding key must not fail a deploy: the rail
|
|
2081
|
+
// treats falsy as "no binding key", so normalise rather than reject.
|
|
2082
|
+
bindingKey: import_zod7.z.string().nullish().transform((value) => value || null),
|
|
2083
|
+
accessLevel: import_zod7.z.enum(["read", "write", "admin"])
|
|
2084
|
+
})
|
|
2085
|
+
).default([]),
|
|
2086
|
+
installs: import_zod7.z.record(
|
|
2087
|
+
import_zod7.z.string(),
|
|
2088
|
+
import_zod7.z.strictObject({
|
|
2089
|
+
installedVersion: import_zod7.z.string().min(1),
|
|
2090
|
+
datastoreBindings: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).nullish()
|
|
2091
|
+
})
|
|
2092
|
+
).default({})
|
|
2093
|
+
});
|
|
2094
|
+
|
|
2069
2095
|
// ../../packages/lib/src/shared/module-enablement.ts
|
|
2070
2096
|
function isModuleEnabledByList(raw, name, kindOf) {
|
|
2071
2097
|
if (!raw) return true;
|
|
@@ -3546,6 +3572,7 @@ function planCompositionArtifacts(input) {
|
|
|
3546
3572
|
endpointStubs: stubs.endpointStubs,
|
|
3547
3573
|
routeStubs: stubs.routeStubs,
|
|
3548
3574
|
writes,
|
|
3575
|
+
desiredPaths: [...desired.keys()].sort(compareNames2),
|
|
3549
3576
|
deletes
|
|
3550
3577
|
}
|
|
3551
3578
|
};
|
|
@@ -3555,6 +3582,27 @@ function dirnamePosix(filePath) {
|
|
|
3555
3582
|
return idx <= 0 ? "." : filePath.slice(0, idx);
|
|
3556
3583
|
}
|
|
3557
3584
|
|
|
3585
|
+
// ../../packages/lib/src/server/module-rail/compose-inputs.ts
|
|
3586
|
+
async function buildComposeInputs(input) {
|
|
3587
|
+
const { db, projectId, deps } = input;
|
|
3588
|
+
const connectedStores = await deps.listDatabaseStoresForProject(db, projectId);
|
|
3589
|
+
const installRows = await Promise.all(
|
|
3590
|
+
input.moduleNames.map(async (moduleName) => ({
|
|
3591
|
+
moduleName,
|
|
3592
|
+
install: await deps.getModuleInstall(db, projectId, moduleName)
|
|
3593
|
+
}))
|
|
3594
|
+
);
|
|
3595
|
+
const installs = {};
|
|
3596
|
+
for (const { moduleName, install } of installRows) {
|
|
3597
|
+
if (!install) continue;
|
|
3598
|
+
installs[moduleName] = {
|
|
3599
|
+
installedVersion: install.installedVersion,
|
|
3600
|
+
datastoreBindings: install.datastoreBindings ?? null
|
|
3601
|
+
};
|
|
3602
|
+
}
|
|
3603
|
+
return composeInputsSchema.parse({ connectedStores, installs });
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3558
3606
|
// ../../packages/lib/src/shared/data-store-manifest.ts
|
|
3559
3607
|
function dataStoreSlug(name) {
|
|
3560
3608
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -3751,162 +3799,162 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
|
|
|
3751
3799
|
}
|
|
3752
3800
|
|
|
3753
3801
|
// ../../packages/lib/src/shared/schema-ops.ts
|
|
3754
|
-
var
|
|
3755
|
-
var columnSpecSchema =
|
|
3756
|
-
name:
|
|
3757
|
-
pgType:
|
|
3758
|
-
nullable:
|
|
3802
|
+
var import_zod8 = require("zod");
|
|
3803
|
+
var columnSpecSchema = import_zod8.z.object({
|
|
3804
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3805
|
+
pgType: import_zod8.z.string().min(1),
|
|
3806
|
+
nullable: import_zod8.z.boolean(),
|
|
3759
3807
|
/**
|
|
3760
3808
|
* A SQL expression used as the column default, as a raw string (e.g. `'t'`,
|
|
3761
3809
|
* `0`, `now()`, `gen_random_uuid()`). `null` or omitted means no default.
|
|
3762
3810
|
* NOT a JS literal — must already be a valid SQL expression.
|
|
3763
3811
|
*/
|
|
3764
|
-
default:
|
|
3765
|
-
fieldType:
|
|
3766
|
-
fieldConfig:
|
|
3812
|
+
default: import_zod8.z.string().nullable().optional(),
|
|
3813
|
+
fieldType: import_zod8.z.string().optional(),
|
|
3814
|
+
fieldConfig: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.unknown()).optional()
|
|
3767
3815
|
});
|
|
3768
|
-
var referentialActionSchema =
|
|
3816
|
+
var referentialActionSchema = import_zod8.z.enum([
|
|
3769
3817
|
"cascade",
|
|
3770
3818
|
"set null",
|
|
3771
3819
|
"set default",
|
|
3772
3820
|
"restrict",
|
|
3773
3821
|
"no action"
|
|
3774
3822
|
]);
|
|
3775
|
-
var schemaOpSchema =
|
|
3823
|
+
var schemaOpSchema = import_zod8.z.discriminatedUnion("type", [
|
|
3776
3824
|
// Tables
|
|
3777
|
-
|
|
3778
|
-
type:
|
|
3779
|
-
table:
|
|
3780
|
-
columns:
|
|
3781
|
-
primaryKey:
|
|
3782
|
-
ifNotExists:
|
|
3825
|
+
import_zod8.z.object({
|
|
3826
|
+
type: import_zod8.z.literal("createTable"),
|
|
3827
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3828
|
+
columns: import_zod8.z.array(columnSpecSchema).min(1),
|
|
3829
|
+
primaryKey: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
3830
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3783
3831
|
}),
|
|
3784
|
-
|
|
3785
|
-
type:
|
|
3786
|
-
table:
|
|
3787
|
-
cascade:
|
|
3788
|
-
ifExists:
|
|
3832
|
+
import_zod8.z.object({
|
|
3833
|
+
type: import_zod8.z.literal("dropTable"),
|
|
3834
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3835
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3836
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3789
3837
|
}),
|
|
3790
|
-
|
|
3791
|
-
type:
|
|
3792
|
-
from:
|
|
3793
|
-
to:
|
|
3838
|
+
import_zod8.z.object({
|
|
3839
|
+
type: import_zod8.z.literal("renameTable"),
|
|
3840
|
+
from: import_zod8.z.string().min(1).max(63),
|
|
3841
|
+
to: import_zod8.z.string().min(1).max(63)
|
|
3794
3842
|
}),
|
|
3795
3843
|
// Columns
|
|
3796
|
-
|
|
3797
|
-
type:
|
|
3798
|
-
table:
|
|
3844
|
+
import_zod8.z.object({
|
|
3845
|
+
type: import_zod8.z.literal("addColumn"),
|
|
3846
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3799
3847
|
column: columnSpecSchema,
|
|
3800
|
-
ifNotExists:
|
|
3848
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3801
3849
|
}),
|
|
3802
|
-
|
|
3803
|
-
type:
|
|
3804
|
-
table:
|
|
3805
|
-
column:
|
|
3806
|
-
cascade:
|
|
3807
|
-
ifExists:
|
|
3850
|
+
import_zod8.z.object({
|
|
3851
|
+
type: import_zod8.z.literal("dropColumn"),
|
|
3852
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3853
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3854
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3855
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3808
3856
|
}),
|
|
3809
|
-
|
|
3810
|
-
type:
|
|
3811
|
-
table:
|
|
3812
|
-
from:
|
|
3813
|
-
to:
|
|
3857
|
+
import_zod8.z.object({
|
|
3858
|
+
type: import_zod8.z.literal("renameColumn"),
|
|
3859
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3860
|
+
from: import_zod8.z.string().min(1).max(63),
|
|
3861
|
+
to: import_zod8.z.string().min(1).max(63)
|
|
3814
3862
|
}),
|
|
3815
|
-
|
|
3816
|
-
type:
|
|
3817
|
-
table:
|
|
3818
|
-
column:
|
|
3819
|
-
newType:
|
|
3863
|
+
import_zod8.z.object({
|
|
3864
|
+
type: import_zod8.z.literal("alterColumnType"),
|
|
3865
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3866
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3867
|
+
newType: import_zod8.z.string().min(1),
|
|
3820
3868
|
/** Optional USING expression for potentially-lossy casts */
|
|
3821
|
-
using:
|
|
3869
|
+
using: import_zod8.z.string().optional()
|
|
3822
3870
|
}),
|
|
3823
|
-
|
|
3824
|
-
type:
|
|
3825
|
-
table:
|
|
3826
|
-
column:
|
|
3827
|
-
nullable:
|
|
3871
|
+
import_zod8.z.object({
|
|
3872
|
+
type: import_zod8.z.literal("alterColumnNullable"),
|
|
3873
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3874
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3875
|
+
nullable: import_zod8.z.boolean()
|
|
3828
3876
|
}),
|
|
3829
|
-
|
|
3830
|
-
type:
|
|
3831
|
-
table:
|
|
3832
|
-
column:
|
|
3877
|
+
import_zod8.z.object({
|
|
3878
|
+
type: import_zod8.z.literal("alterColumnDefault"),
|
|
3879
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3880
|
+
column: import_zod8.z.string().min(1).max(63),
|
|
3833
3881
|
/** null means drop the default; otherwise a SQL expression string. */
|
|
3834
|
-
default:
|
|
3882
|
+
default: import_zod8.z.string().nullable()
|
|
3835
3883
|
}),
|
|
3836
3884
|
// Indexes
|
|
3837
|
-
|
|
3838
|
-
type:
|
|
3839
|
-
table:
|
|
3840
|
-
name:
|
|
3841
|
-
columns:
|
|
3842
|
-
unique:
|
|
3885
|
+
import_zod8.z.object({
|
|
3886
|
+
type: import_zod8.z.literal("addIndex"),
|
|
3887
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3888
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3889
|
+
columns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1),
|
|
3890
|
+
unique: import_zod8.z.boolean().optional(),
|
|
3843
3891
|
/** Partial-index WHERE clause, as a raw SQL expression */
|
|
3844
|
-
where:
|
|
3845
|
-
ifNotExists:
|
|
3892
|
+
where: import_zod8.z.string().optional(),
|
|
3893
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3846
3894
|
}),
|
|
3847
|
-
|
|
3848
|
-
type:
|
|
3849
|
-
name:
|
|
3850
|
-
cascade:
|
|
3851
|
-
ifExists:
|
|
3895
|
+
import_zod8.z.object({
|
|
3896
|
+
type: import_zod8.z.literal("dropIndex"),
|
|
3897
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3898
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3899
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3852
3900
|
}),
|
|
3853
3901
|
// Constraints
|
|
3854
|
-
|
|
3855
|
-
type:
|
|
3856
|
-
table:
|
|
3857
|
-
name:
|
|
3858
|
-
columns:
|
|
3859
|
-
refTable:
|
|
3860
|
-
refColumns:
|
|
3902
|
+
import_zod8.z.object({
|
|
3903
|
+
type: import_zod8.z.literal("addForeignKey"),
|
|
3904
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3905
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3906
|
+
columns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1),
|
|
3907
|
+
refTable: import_zod8.z.string().min(1).max(63),
|
|
3908
|
+
refColumns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1),
|
|
3861
3909
|
onDelete: referentialActionSchema.optional(),
|
|
3862
3910
|
onUpdate: referentialActionSchema.optional()
|
|
3863
3911
|
}),
|
|
3864
|
-
|
|
3865
|
-
type:
|
|
3866
|
-
table:
|
|
3867
|
-
name:
|
|
3868
|
-
columns:
|
|
3912
|
+
import_zod8.z.object({
|
|
3913
|
+
type: import_zod8.z.literal("addUniqueConstraint"),
|
|
3914
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3915
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3916
|
+
columns: import_zod8.z.array(import_zod8.z.string().min(1)).min(1)
|
|
3869
3917
|
}),
|
|
3870
|
-
|
|
3871
|
-
type:
|
|
3872
|
-
table:
|
|
3873
|
-
name:
|
|
3874
|
-
expression:
|
|
3918
|
+
import_zod8.z.object({
|
|
3919
|
+
type: import_zod8.z.literal("addCheckConstraint"),
|
|
3920
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3921
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3922
|
+
expression: import_zod8.z.string().min(1)
|
|
3875
3923
|
}),
|
|
3876
|
-
|
|
3877
|
-
type:
|
|
3878
|
-
table:
|
|
3879
|
-
name:
|
|
3880
|
-
cascade:
|
|
3881
|
-
ifExists:
|
|
3924
|
+
import_zod8.z.object({
|
|
3925
|
+
type: import_zod8.z.literal("dropConstraint"),
|
|
3926
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3927
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3928
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3929
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3882
3930
|
}),
|
|
3883
3931
|
// Enum types
|
|
3884
|
-
|
|
3885
|
-
type:
|
|
3886
|
-
name:
|
|
3887
|
-
values:
|
|
3932
|
+
import_zod8.z.object({
|
|
3933
|
+
type: import_zod8.z.literal("createEnum"),
|
|
3934
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3935
|
+
values: import_zod8.z.array(import_zod8.z.string().min(1)).min(1)
|
|
3888
3936
|
}),
|
|
3889
|
-
|
|
3890
|
-
type:
|
|
3891
|
-
name:
|
|
3892
|
-
value:
|
|
3937
|
+
import_zod8.z.object({
|
|
3938
|
+
type: import_zod8.z.literal("alterEnumAddValue"),
|
|
3939
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3940
|
+
value: import_zod8.z.string().min(1),
|
|
3893
3941
|
/** Position the new value before an existing one (mutually exclusive with `after`) */
|
|
3894
|
-
before:
|
|
3942
|
+
before: import_zod8.z.string().optional(),
|
|
3895
3943
|
/** Position the new value after an existing one (mutually exclusive with `before`) */
|
|
3896
|
-
after:
|
|
3897
|
-
ifNotExists:
|
|
3944
|
+
after: import_zod8.z.string().optional(),
|
|
3945
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3898
3946
|
}),
|
|
3899
|
-
|
|
3900
|
-
type:
|
|
3901
|
-
name:
|
|
3902
|
-
cascade:
|
|
3903
|
-
ifExists:
|
|
3947
|
+
import_zod8.z.object({
|
|
3948
|
+
type: import_zod8.z.literal("dropEnum"),
|
|
3949
|
+
name: import_zod8.z.string().min(1).max(63),
|
|
3950
|
+
cascade: import_zod8.z.boolean().optional(),
|
|
3951
|
+
ifExists: import_zod8.z.boolean().optional()
|
|
3904
3952
|
}),
|
|
3905
3953
|
// Extensions
|
|
3906
|
-
|
|
3907
|
-
type:
|
|
3908
|
-
name:
|
|
3909
|
-
ifNotExists:
|
|
3954
|
+
import_zod8.z.object({
|
|
3955
|
+
type: import_zod8.z.literal("createExtension"),
|
|
3956
|
+
name: import_zod8.z.string().min(1),
|
|
3957
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3910
3958
|
}),
|
|
3911
3959
|
// Data operations
|
|
3912
3960
|
/**
|
|
@@ -3914,26 +3962,26 @@ var schemaOpSchema = import_zod7.z.discriminatedUnion("type", [
|
|
|
3914
3962
|
* destructive — requires explicit confirm to replay on prod. The
|
|
3915
3963
|
* `description` is what the agent/user sees at confirm time.
|
|
3916
3964
|
*/
|
|
3917
|
-
|
|
3918
|
-
type:
|
|
3919
|
-
description:
|
|
3920
|
-
sql:
|
|
3965
|
+
import_zod8.z.object({
|
|
3966
|
+
type: import_zod8.z.literal("backfill"),
|
|
3967
|
+
description: import_zod8.z.string().min(1),
|
|
3968
|
+
sql: import_zod8.z.string().min(1)
|
|
3921
3969
|
}),
|
|
3922
3970
|
/**
|
|
3923
3971
|
* Escape hatch for DDL the structured ops can't express. `destructive`
|
|
3924
3972
|
* forces classification — use false only for genuinely additive-only SQL
|
|
3925
3973
|
* (e.g. CREATE EXTENSION variants the structured op doesn't cover).
|
|
3926
3974
|
*/
|
|
3927
|
-
|
|
3928
|
-
type:
|
|
3929
|
-
description:
|
|
3930
|
-
sql:
|
|
3931
|
-
destructive:
|
|
3975
|
+
import_zod8.z.object({
|
|
3976
|
+
type: import_zod8.z.literal("rawSql"),
|
|
3977
|
+
description: import_zod8.z.string().min(1),
|
|
3978
|
+
sql: import_zod8.z.string().min(1),
|
|
3979
|
+
destructive: import_zod8.z.boolean()
|
|
3932
3980
|
})
|
|
3933
3981
|
]);
|
|
3934
3982
|
|
|
3935
3983
|
// ../../packages/lib/src/server/module-rail/ops.ts
|
|
3936
|
-
var
|
|
3984
|
+
var import_zod9 = require("zod");
|
|
3937
3985
|
|
|
3938
3986
|
// ../../packages/lib/src/server/module-rail/reconcile.ts
|
|
3939
3987
|
var SANDBOX_BATCH_SIZE = 200;
|
|
@@ -3974,8 +4022,8 @@ function countedCompositionContext(context) {
|
|
|
3974
4022
|
}
|
|
3975
4023
|
};
|
|
3976
4024
|
}
|
|
3977
|
-
async function listPresentModuleNames(
|
|
3978
|
-
const entries = await listImmediateEntries(
|
|
4025
|
+
async function listPresentModuleNames(sandbox) {
|
|
4026
|
+
const entries = await listImmediateEntries(sandbox, MODULES_SANDBOX_DIR, {
|
|
3979
4027
|
optional: true,
|
|
3980
4028
|
label: "modules"
|
|
3981
4029
|
});
|
|
@@ -4179,24 +4227,24 @@ async function discoverModuleRouteEntries(context, moduleName) {
|
|
|
4179
4227
|
}
|
|
4180
4228
|
return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
|
|
4181
4229
|
}
|
|
4182
|
-
async function writeSandboxFileAtomic(
|
|
4230
|
+
async function writeSandboxFileAtomic(target, path3, content) {
|
|
4183
4231
|
const dir = dirnamePosix(path3);
|
|
4184
|
-
const tmp = `${path3}.tmp.${
|
|
4232
|
+
const tmp = `${path3}.tmp.${target.runId}`;
|
|
4185
4233
|
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
4186
4234
|
const cmd = [
|
|
4187
4235
|
`mkdir -p ${shellQuote(dir)}`,
|
|
4188
4236
|
`printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
|
|
4189
4237
|
`mv -f ${shellQuote(tmp)} ${shellQuote(path3)}`
|
|
4190
4238
|
].join(" && ");
|
|
4191
|
-
const result = await
|
|
4239
|
+
const result = await target.sandbox.exec(cmd, { raiseOnError: false });
|
|
4192
4240
|
if (result.exitCode !== 0) {
|
|
4193
|
-
const cleanup = await
|
|
4241
|
+
const cleanup = await target.sandbox.exec(`rm -f ${shellQuote(tmp)}`, {
|
|
4194
4242
|
raiseOnError: false
|
|
4195
4243
|
});
|
|
4196
4244
|
if (cleanup.exitCode !== 0) {
|
|
4197
4245
|
const cleanupDetail = typeof cleanup.error === "string" && cleanup.error.trim() || cleanup.output.trim() || `exit ${cleanup.exitCode}`;
|
|
4198
4246
|
console.log(
|
|
4199
|
-
`[ModuleRail] runId=${
|
|
4247
|
+
`[ModuleRail] runId=${target.runId} composition artifacts failed to clean temp ${redactSecrets(tmp)}: ${redactSecrets(cleanupDetail)}`
|
|
4200
4248
|
);
|
|
4201
4249
|
}
|
|
4202
4250
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
@@ -4254,6 +4302,40 @@ async function deleteSandboxFile(context, path3) {
|
|
|
4254
4302
|
);
|
|
4255
4303
|
}
|
|
4256
4304
|
}
|
|
4305
|
+
async function writeComposeInputs(target) {
|
|
4306
|
+
const ignored = await target.sandbox.exec(
|
|
4307
|
+
`git check-ignore -q ${shellQuote(COMPOSE_INPUTS_PATH)}`,
|
|
4308
|
+
{ raiseOnError: false }
|
|
4309
|
+
);
|
|
4310
|
+
if (ignored.exitCode === 1) {
|
|
4311
|
+
console.log(
|
|
4312
|
+
`[ComposeInputs] project=${target.projectId} skipped: ${COMPOSE_INPUTS_PATH} is not ignored in this checkout`
|
|
4313
|
+
);
|
|
4314
|
+
return false;
|
|
4315
|
+
}
|
|
4316
|
+
if (ignored.exitCode !== 0) {
|
|
4317
|
+
const detail = typeof ignored.error === "string" && ignored.error.trim() || ignored.output.trim() || "(no output)";
|
|
4318
|
+
throw new Error(
|
|
4319
|
+
`[ComposeInputs] git check-ignore failed in this checkout (exit ${ignored.exitCode}): ${redactSecrets(detail)}`
|
|
4320
|
+
);
|
|
4321
|
+
}
|
|
4322
|
+
const moduleNames = await listPresentModuleNames(target.sandbox);
|
|
4323
|
+
const inputs = await buildComposeInputs({
|
|
4324
|
+
db: target.db,
|
|
4325
|
+
projectId: target.projectId,
|
|
4326
|
+
deps: target.deps,
|
|
4327
|
+
moduleNames
|
|
4328
|
+
});
|
|
4329
|
+
const content = `${JSON.stringify(inputs, null, 2)}
|
|
4330
|
+
`;
|
|
4331
|
+
const existing = await target.sandbox.fileExists(COMPOSE_INPUTS_PATH) ? await target.sandbox.readFile(COMPOSE_INPUTS_PATH) : null;
|
|
4332
|
+
const written = existing === null || existing.trimEnd() !== content.trimEnd();
|
|
4333
|
+
if (written) await writeSandboxFileAtomic(target, COMPOSE_INPUTS_PATH, content);
|
|
4334
|
+
console.log(
|
|
4335
|
+
`[ComposeInputs] project=${target.projectId} stores=${inputs.connectedStores.length} installs=${Object.keys(inputs.installs).length} written=${written}`
|
|
4336
|
+
);
|
|
4337
|
+
return written;
|
|
4338
|
+
}
|
|
4257
4339
|
async function applyCompositionArtifactMutations(context, plan, existingFiles) {
|
|
4258
4340
|
const appliedWrites = [];
|
|
4259
4341
|
const appliedDeletes = [];
|
|
@@ -4889,7 +4971,7 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
4889
4971
|
context = counted.context;
|
|
4890
4972
|
const deps = context.deps;
|
|
4891
4973
|
if (!deps) throw new Error("Module rail dependencies are required for composition artifacts");
|
|
4892
|
-
const presentRaw = await listPresentModuleNames(context);
|
|
4974
|
+
const presentRaw = await listPresentModuleNames(context.sandbox);
|
|
4893
4975
|
const presentNames = presentRaw.filter((name) => isValidModuleName(name)).sort((a, b) => a.localeCompare(b));
|
|
4894
4976
|
const moduleFacts = [];
|
|
4895
4977
|
const manifestsByName = {};
|
|
@@ -5109,6 +5191,15 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
5109
5191
|
console.log(
|
|
5110
5192
|
`[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}`
|
|
5111
5193
|
);
|
|
5194
|
+
if (!options.dryRun && !options.skipComposeInputs) {
|
|
5195
|
+
await writeComposeInputs({
|
|
5196
|
+
db: context.db,
|
|
5197
|
+
projectId: context.projectId,
|
|
5198
|
+
runId: context.runId,
|
|
5199
|
+
sandbox: context.sandbox,
|
|
5200
|
+
deps
|
|
5201
|
+
});
|
|
5202
|
+
}
|
|
5112
5203
|
if (changed && !options.dryRun) {
|
|
5113
5204
|
await applyCompositionArtifactMutations(context, planned.plan, existingFiles);
|
|
5114
5205
|
}
|
|
@@ -5116,6 +5207,7 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
5116
5207
|
registryPath: planned.plan.registryPath,
|
|
5117
5208
|
writePaths: planned.plan.writes.map((w) => w.path),
|
|
5118
5209
|
deletePaths: planned.plan.deletes,
|
|
5210
|
+
artifactPaths: planned.plan.desiredPaths,
|
|
5119
5211
|
changed,
|
|
5120
5212
|
composedModules: composedNames,
|
|
5121
5213
|
excludedModules
|
|
@@ -5126,11 +5218,75 @@ var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
|
|
|
5126
5218
|
|
|
5127
5219
|
// src/index.ts
|
|
5128
5220
|
var MODULES_DIR2 = MODULES_SANDBOX_DIR;
|
|
5221
|
+
function readComposeInputs(cwd) {
|
|
5222
|
+
const file = import_node_path2.default.join(cwd, COMPOSE_INPUTS_PATH);
|
|
5223
|
+
if (!import_node_fs2.default.existsSync(file)) return { connectedStores: [], installs: {} };
|
|
5224
|
+
let raw;
|
|
5225
|
+
try {
|
|
5226
|
+
raw = JSON.parse(import_node_fs2.default.readFileSync(file, "utf8"));
|
|
5227
|
+
} catch (error) {
|
|
5228
|
+
throw new Error(
|
|
5229
|
+
`[compose] ${file} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
5230
|
+
);
|
|
5231
|
+
}
|
|
5232
|
+
const parsed = composeInputsSchema.safeParse(raw);
|
|
5233
|
+
if (!parsed.success) {
|
|
5234
|
+
const issues = parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
|
|
5235
|
+
throw new Error(`[compose] ${file} is invalid: ${issues}`);
|
|
5236
|
+
}
|
|
5237
|
+
return {
|
|
5238
|
+
connectedStores: parsed.data.connectedStores.map((store) => ({
|
|
5239
|
+
storeId: store.storeId,
|
|
5240
|
+
slug: store.slug,
|
|
5241
|
+
bindingKey: store.bindingKey ?? null,
|
|
5242
|
+
accessLevel: store.accessLevel
|
|
5243
|
+
})),
|
|
5244
|
+
installs: parsed.data.installs
|
|
5245
|
+
};
|
|
5246
|
+
}
|
|
5247
|
+
function assertSupportedTypescript(versionMajorMinor) {
|
|
5248
|
+
const major = Number.parseInt(versionMajorMinor.split(".")[0] ?? "", 10);
|
|
5249
|
+
if (!Number.isInteger(major) || major < 5 || major >= 7) {
|
|
5250
|
+
throw new Error(`[compose] typescript ${versionMajorMinor} is not supported (need >=5 <7)`);
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
var APP_DIR_PREFIX2 = "apps/web/src/app/";
|
|
5254
|
+
var APP_PACKAGE_JSON = "apps/web/package.json";
|
|
5255
|
+
var STUB_GITIGNORE_PATH = "apps/web/src/app/.gitignore";
|
|
5256
|
+
var STUB_GITIGNORE_HEADER = "# Generated by stardeck-compose \u2014 Module-rail stubs are build outputs; do not edit or commit them.";
|
|
5257
|
+
function escapeGitignore(pattern) {
|
|
5258
|
+
const escaped = pattern.replace(/[\\[\]*?]/g, (char) => `\\${char}`);
|
|
5259
|
+
return /^[#!]/.test(escaped) ? `\\${escaped}` : escaped;
|
|
5260
|
+
}
|
|
5261
|
+
function stubGitignoreEntries(artifactPaths) {
|
|
5262
|
+
return artifactPaths.filter((artifact) => artifact.startsWith(APP_DIR_PREFIX2)).map((artifact) => escapeGitignore(`/${artifact.slice(APP_DIR_PREFIX2.length)}`)).sort();
|
|
5263
|
+
}
|
|
5264
|
+
function writeStubGitignore(cwd, artifactPaths) {
|
|
5265
|
+
const entries = stubGitignoreEntries(artifactPaths);
|
|
5266
|
+
const content = [STUB_GITIGNORE_HEADER, ...entries].join("\n") + "\n";
|
|
5267
|
+
const file = import_node_path2.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
5268
|
+
const existing = import_node_fs2.default.existsSync(file) ? import_node_fs2.default.readFileSync(file, "utf8") : null;
|
|
5269
|
+
if (existing !== content) {
|
|
5270
|
+
import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
|
|
5271
|
+
import_node_fs2.default.writeFileSync(file, content);
|
|
5272
|
+
}
|
|
5273
|
+
return entries.length;
|
|
5274
|
+
}
|
|
5275
|
+
function assertStubGitignoreOwned(cwd) {
|
|
5276
|
+
const file = import_node_path2.default.join(cwd, STUB_GITIGNORE_PATH);
|
|
5277
|
+
if (!import_node_fs2.default.existsSync(file)) return;
|
|
5278
|
+
const firstLine = import_node_fs2.default.readFileSync(file, "utf8").split("\n", 1)[0] ?? "";
|
|
5279
|
+
if (firstLine !== STUB_GITIGNORE_HEADER) {
|
|
5280
|
+
throw new Error(
|
|
5281
|
+
`[compose] refusing to overwrite ${STUB_GITIGNORE_PATH}: it is not compose-generated (line 1 is not the generated header)`
|
|
5282
|
+
);
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5129
5285
|
function assertAppRoot(cwd) {
|
|
5130
|
-
const
|
|
5131
|
-
if (!import_node_fs2.default.existsSync(
|
|
5286
|
+
const appPath = import_node_path2.default.join(cwd, APP_PACKAGE_JSON);
|
|
5287
|
+
if (!import_node_fs2.default.existsSync(appPath)) {
|
|
5132
5288
|
throw new Error(
|
|
5133
|
-
`[compose] ${
|
|
5289
|
+
`[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
|
|
5134
5290
|
);
|
|
5135
5291
|
}
|
|
5136
5292
|
}
|
|
@@ -5210,12 +5366,14 @@ function removeDir(absolute) {
|
|
|
5210
5366
|
}
|
|
5211
5367
|
async function compose(options) {
|
|
5212
5368
|
assertAppRoot(options.cwd);
|
|
5369
|
+
assertStubGitignoreOwned(options.cwd);
|
|
5370
|
+
const inputs = readComposeInputs(options.cwd);
|
|
5213
5371
|
if (options.prune) assertModuleTreeClean(options.cwd);
|
|
5214
5372
|
const sandbox = localFsSandbox(options.cwd);
|
|
5215
5373
|
const deps = {
|
|
5216
5374
|
sandbox,
|
|
5217
|
-
getModuleInstall: async () => null,
|
|
5218
|
-
listDatabaseStoresForProject: async () =>
|
|
5375
|
+
getModuleInstall: async (_db, _projectId, moduleName) => inputs.installs[moduleName] ?? null,
|
|
5376
|
+
listDatabaseStoresForProject: async () => inputs.connectedStores
|
|
5219
5377
|
};
|
|
5220
5378
|
const context = {
|
|
5221
5379
|
db: {},
|
|
@@ -5232,8 +5390,12 @@ async function compose(options) {
|
|
|
5232
5390
|
assertNoAppLayerImporters(options.cwd, preview.excludedModules);
|
|
5233
5391
|
}
|
|
5234
5392
|
const result = await reconcileCompositionArtifacts(context, {
|
|
5235
|
-
enabledModulesRaw: options.enabled
|
|
5393
|
+
enabledModulesRaw: options.enabled,
|
|
5394
|
+
// compose is the inputs file's reader: the deps above echo it back, so
|
|
5395
|
+
// letting the rail rewrite it would round-trip a fork's own input.
|
|
5396
|
+
skipComposeInputs: true
|
|
5236
5397
|
});
|
|
5398
|
+
const gitignoreEntries = options.enabled ? null : writeStubGitignore(options.cwd, result.artifactPaths);
|
|
5237
5399
|
let pruned = 0;
|
|
5238
5400
|
if (options.prune) {
|
|
5239
5401
|
for (const name of result.excludedModules) {
|
|
@@ -5250,12 +5412,14 @@ async function compose(options) {
|
|
|
5250
5412
|
excludedModules: result.excludedModules,
|
|
5251
5413
|
writes: result.writePaths.length,
|
|
5252
5414
|
deletes: result.deletePaths.length,
|
|
5253
|
-
pruned
|
|
5415
|
+
pruned,
|
|
5416
|
+
gitignoreEntries
|
|
5254
5417
|
};
|
|
5255
5418
|
}
|
|
5256
5419
|
|
|
5257
5420
|
// src/cli.ts
|
|
5258
5421
|
async function main() {
|
|
5422
|
+
assertSupportedTypescript(import_typescript5.default.versionMajorMinor);
|
|
5259
5423
|
const { values } = (0, import_node_util.parseArgs)({
|
|
5260
5424
|
options: {
|
|
5261
5425
|
enabled: { type: "string" },
|
|
@@ -5276,7 +5440,7 @@ async function main() {
|
|
|
5276
5440
|
prune: values.prune
|
|
5277
5441
|
});
|
|
5278
5442
|
console.log(
|
|
5279
|
-
`[compose] modules=${result.composedModules.join(",") || "none"} excluded=${result.excludedModules.join(",") || "none"} writes=${result.writes} deletes=${result.deletes} pruned=${result.pruned}`
|
|
5443
|
+
`[compose] modules=${result.composedModules.join(",") || "none"} excluded=${result.excludedModules.join(",") || "none"} writes=${result.writes} deletes=${result.deletes} pruned=${result.pruned} gitignore=${result.gitignoreEntries ?? "kept"} stub paths`
|
|
5280
5444
|
);
|
|
5281
5445
|
}
|
|
5282
5446
|
main().catch((error) => {
|