@stardeck-customer-apps/compose 0.2.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 +10 -6
- package/dist/cli.js +225 -153
- package/dist/index.d.mts +33 -20
- package/dist/index.d.ts +33 -20
- package/dist/index.js +225 -153
- package/dist/index.mjs +225 -153
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -23,10 +23,13 @@ Compose turns that declaration into the code Next.js actually builds:
|
|
|
23
23
|
npx stardeck-compose [--enabled=a,b,c] [--prune] [--cwd=<repo root>]
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Run it from the repo root (the directory holding `apps/web`).
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
Run it from the repo root (the directory holding `apps/web`). The Blueprint
|
|
27
|
+
island wires `predev`, `prebuild`, `prebuild:check`, `prelint`, `pretypecheck`
|
|
28
|
+
and `pretest` in `apps/web/package.json` to `stardeck-compose --cwd ../..`, and
|
|
29
|
+
a platform migration adds the same hooks to every app, so every
|
|
30
|
+
`npm run dev|build|lint|typecheck|test` composes first. A repo with no
|
|
31
|
+
`apps/web/src/modules` is a valid app: it composes to empty registries and a
|
|
32
|
+
header-only stub ignore list.
|
|
30
33
|
|
|
31
34
|
Needs `typescript` >=5 <7 (it parses your route entries with the compiler API).
|
|
32
35
|
|
|
@@ -45,8 +48,9 @@ from `apps/web/stardeck.compose.json` instead:
|
|
|
45
48
|
}
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
The platform writes it
|
|
49
|
-
|
|
51
|
+
The platform writes it before every deploy, every dev-server start or restart,
|
|
52
|
+
and every rail reconcile; write it by hand to compose locally against real
|
|
53
|
+
stores. It is an input, not a result — compose still resolves and
|
|
50
54
|
validates the bindings itself. Without the file, `module-datastores.gen.ts`
|
|
51
55
|
composes empty and every registry version reads `unreleased`, so an app with
|
|
52
56
|
bound stores must not compose without it. A malformed file is a hard error, never
|
package/dist/cli.js
CHANGED
|
@@ -2069,6 +2069,29 @@ var initialModuleCompositionSchema = import_zod6.z.object({
|
|
|
2069
2069
|
});
|
|
2070
2070
|
var MODULE_SETUP_ATTEMPT_STALE_MS = 10 * 60 * 1e3;
|
|
2071
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
|
+
|
|
2072
2095
|
// ../../packages/lib/src/shared/module-enablement.ts
|
|
2073
2096
|
function isModuleEnabledByList(raw, name, kindOf) {
|
|
2074
2097
|
if (!raw) return true;
|
|
@@ -3559,6 +3582,27 @@ function dirnamePosix(filePath) {
|
|
|
3559
3582
|
return idx <= 0 ? "." : filePath.slice(0, idx);
|
|
3560
3583
|
}
|
|
3561
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
|
+
|
|
3562
3606
|
// ../../packages/lib/src/shared/data-store-manifest.ts
|
|
3563
3607
|
function dataStoreSlug(name) {
|
|
3564
3608
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -3755,162 +3799,162 @@ function rejectPseudoModuleEntries(entries, parentDir, label) {
|
|
|
3755
3799
|
}
|
|
3756
3800
|
|
|
3757
3801
|
// ../../packages/lib/src/shared/schema-ops.ts
|
|
3758
|
-
var
|
|
3759
|
-
var columnSpecSchema =
|
|
3760
|
-
name:
|
|
3761
|
-
pgType:
|
|
3762
|
-
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(),
|
|
3763
3807
|
/**
|
|
3764
3808
|
* A SQL expression used as the column default, as a raw string (e.g. `'t'`,
|
|
3765
3809
|
* `0`, `now()`, `gen_random_uuid()`). `null` or omitted means no default.
|
|
3766
3810
|
* NOT a JS literal — must already be a valid SQL expression.
|
|
3767
3811
|
*/
|
|
3768
|
-
default:
|
|
3769
|
-
fieldType:
|
|
3770
|
-
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()
|
|
3771
3815
|
});
|
|
3772
|
-
var referentialActionSchema =
|
|
3816
|
+
var referentialActionSchema = import_zod8.z.enum([
|
|
3773
3817
|
"cascade",
|
|
3774
3818
|
"set null",
|
|
3775
3819
|
"set default",
|
|
3776
3820
|
"restrict",
|
|
3777
3821
|
"no action"
|
|
3778
3822
|
]);
|
|
3779
|
-
var schemaOpSchema =
|
|
3823
|
+
var schemaOpSchema = import_zod8.z.discriminatedUnion("type", [
|
|
3780
3824
|
// Tables
|
|
3781
|
-
|
|
3782
|
-
type:
|
|
3783
|
-
table:
|
|
3784
|
-
columns:
|
|
3785
|
-
primaryKey:
|
|
3786
|
-
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()
|
|
3787
3831
|
}),
|
|
3788
|
-
|
|
3789
|
-
type:
|
|
3790
|
-
table:
|
|
3791
|
-
cascade:
|
|
3792
|
-
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()
|
|
3793
3837
|
}),
|
|
3794
|
-
|
|
3795
|
-
type:
|
|
3796
|
-
from:
|
|
3797
|
-
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)
|
|
3798
3842
|
}),
|
|
3799
3843
|
// Columns
|
|
3800
|
-
|
|
3801
|
-
type:
|
|
3802
|
-
table:
|
|
3844
|
+
import_zod8.z.object({
|
|
3845
|
+
type: import_zod8.z.literal("addColumn"),
|
|
3846
|
+
table: import_zod8.z.string().min(1).max(63),
|
|
3803
3847
|
column: columnSpecSchema,
|
|
3804
|
-
ifNotExists:
|
|
3848
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3805
3849
|
}),
|
|
3806
|
-
|
|
3807
|
-
type:
|
|
3808
|
-
table:
|
|
3809
|
-
column:
|
|
3810
|
-
cascade:
|
|
3811
|
-
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()
|
|
3812
3856
|
}),
|
|
3813
|
-
|
|
3814
|
-
type:
|
|
3815
|
-
table:
|
|
3816
|
-
from:
|
|
3817
|
-
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)
|
|
3818
3862
|
}),
|
|
3819
|
-
|
|
3820
|
-
type:
|
|
3821
|
-
table:
|
|
3822
|
-
column:
|
|
3823
|
-
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),
|
|
3824
3868
|
/** Optional USING expression for potentially-lossy casts */
|
|
3825
|
-
using:
|
|
3869
|
+
using: import_zod8.z.string().optional()
|
|
3826
3870
|
}),
|
|
3827
|
-
|
|
3828
|
-
type:
|
|
3829
|
-
table:
|
|
3830
|
-
column:
|
|
3831
|
-
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()
|
|
3832
3876
|
}),
|
|
3833
|
-
|
|
3834
|
-
type:
|
|
3835
|
-
table:
|
|
3836
|
-
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),
|
|
3837
3881
|
/** null means drop the default; otherwise a SQL expression string. */
|
|
3838
|
-
default:
|
|
3882
|
+
default: import_zod8.z.string().nullable()
|
|
3839
3883
|
}),
|
|
3840
3884
|
// Indexes
|
|
3841
|
-
|
|
3842
|
-
type:
|
|
3843
|
-
table:
|
|
3844
|
-
name:
|
|
3845
|
-
columns:
|
|
3846
|
-
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(),
|
|
3847
3891
|
/** Partial-index WHERE clause, as a raw SQL expression */
|
|
3848
|
-
where:
|
|
3849
|
-
ifNotExists:
|
|
3892
|
+
where: import_zod8.z.string().optional(),
|
|
3893
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3850
3894
|
}),
|
|
3851
|
-
|
|
3852
|
-
type:
|
|
3853
|
-
name:
|
|
3854
|
-
cascade:
|
|
3855
|
-
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()
|
|
3856
3900
|
}),
|
|
3857
3901
|
// Constraints
|
|
3858
|
-
|
|
3859
|
-
type:
|
|
3860
|
-
table:
|
|
3861
|
-
name:
|
|
3862
|
-
columns:
|
|
3863
|
-
refTable:
|
|
3864
|
-
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),
|
|
3865
3909
|
onDelete: referentialActionSchema.optional(),
|
|
3866
3910
|
onUpdate: referentialActionSchema.optional()
|
|
3867
3911
|
}),
|
|
3868
|
-
|
|
3869
|
-
type:
|
|
3870
|
-
table:
|
|
3871
|
-
name:
|
|
3872
|
-
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)
|
|
3873
3917
|
}),
|
|
3874
|
-
|
|
3875
|
-
type:
|
|
3876
|
-
table:
|
|
3877
|
-
name:
|
|
3878
|
-
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)
|
|
3879
3923
|
}),
|
|
3880
|
-
|
|
3881
|
-
type:
|
|
3882
|
-
table:
|
|
3883
|
-
name:
|
|
3884
|
-
cascade:
|
|
3885
|
-
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()
|
|
3886
3930
|
}),
|
|
3887
3931
|
// Enum types
|
|
3888
|
-
|
|
3889
|
-
type:
|
|
3890
|
-
name:
|
|
3891
|
-
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)
|
|
3892
3936
|
}),
|
|
3893
|
-
|
|
3894
|
-
type:
|
|
3895
|
-
name:
|
|
3896
|
-
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),
|
|
3897
3941
|
/** Position the new value before an existing one (mutually exclusive with `after`) */
|
|
3898
|
-
before:
|
|
3942
|
+
before: import_zod8.z.string().optional(),
|
|
3899
3943
|
/** Position the new value after an existing one (mutually exclusive with `before`) */
|
|
3900
|
-
after:
|
|
3901
|
-
ifNotExists:
|
|
3944
|
+
after: import_zod8.z.string().optional(),
|
|
3945
|
+
ifNotExists: import_zod8.z.boolean().optional()
|
|
3902
3946
|
}),
|
|
3903
|
-
|
|
3904
|
-
type:
|
|
3905
|
-
name:
|
|
3906
|
-
cascade:
|
|
3907
|
-
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()
|
|
3908
3952
|
}),
|
|
3909
3953
|
// Extensions
|
|
3910
|
-
|
|
3911
|
-
type:
|
|
3912
|
-
name:
|
|
3913
|
-
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()
|
|
3914
3958
|
}),
|
|
3915
3959
|
// Data operations
|
|
3916
3960
|
/**
|
|
@@ -3918,26 +3962,26 @@ var schemaOpSchema = import_zod7.z.discriminatedUnion("type", [
|
|
|
3918
3962
|
* destructive — requires explicit confirm to replay on prod. The
|
|
3919
3963
|
* `description` is what the agent/user sees at confirm time.
|
|
3920
3964
|
*/
|
|
3921
|
-
|
|
3922
|
-
type:
|
|
3923
|
-
description:
|
|
3924
|
-
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)
|
|
3925
3969
|
}),
|
|
3926
3970
|
/**
|
|
3927
3971
|
* Escape hatch for DDL the structured ops can't express. `destructive`
|
|
3928
3972
|
* forces classification — use false only for genuinely additive-only SQL
|
|
3929
3973
|
* (e.g. CREATE EXTENSION variants the structured op doesn't cover).
|
|
3930
3974
|
*/
|
|
3931
|
-
|
|
3932
|
-
type:
|
|
3933
|
-
description:
|
|
3934
|
-
sql:
|
|
3935
|
-
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()
|
|
3936
3980
|
})
|
|
3937
3981
|
]);
|
|
3938
3982
|
|
|
3939
3983
|
// ../../packages/lib/src/server/module-rail/ops.ts
|
|
3940
|
-
var
|
|
3984
|
+
var import_zod9 = require("zod");
|
|
3941
3985
|
|
|
3942
3986
|
// ../../packages/lib/src/server/module-rail/reconcile.ts
|
|
3943
3987
|
var SANDBOX_BATCH_SIZE = 200;
|
|
@@ -3978,8 +4022,8 @@ function countedCompositionContext(context) {
|
|
|
3978
4022
|
}
|
|
3979
4023
|
};
|
|
3980
4024
|
}
|
|
3981
|
-
async function listPresentModuleNames(
|
|
3982
|
-
const entries = await listImmediateEntries(
|
|
4025
|
+
async function listPresentModuleNames(sandbox) {
|
|
4026
|
+
const entries = await listImmediateEntries(sandbox, MODULES_SANDBOX_DIR, {
|
|
3983
4027
|
optional: true,
|
|
3984
4028
|
label: "modules"
|
|
3985
4029
|
});
|
|
@@ -4183,24 +4227,24 @@ async function discoverModuleRouteEntries(context, moduleName) {
|
|
|
4183
4227
|
}
|
|
4184
4228
|
return pages.sort((a, b) => a.nextRelativePath.localeCompare(b.nextRelativePath));
|
|
4185
4229
|
}
|
|
4186
|
-
async function writeSandboxFileAtomic(
|
|
4230
|
+
async function writeSandboxFileAtomic(target, path3, content) {
|
|
4187
4231
|
const dir = dirnamePosix(path3);
|
|
4188
|
-
const tmp = `${path3}.tmp.${
|
|
4232
|
+
const tmp = `${path3}.tmp.${target.runId}`;
|
|
4189
4233
|
const b64 = Buffer.from(content, "utf8").toString("base64");
|
|
4190
4234
|
const cmd = [
|
|
4191
4235
|
`mkdir -p ${shellQuote(dir)}`,
|
|
4192
4236
|
`printf '%s' ${shellQuote(b64)} | base64 -d > ${shellQuote(tmp)}`,
|
|
4193
4237
|
`mv -f ${shellQuote(tmp)} ${shellQuote(path3)}`
|
|
4194
4238
|
].join(" && ");
|
|
4195
|
-
const result = await
|
|
4239
|
+
const result = await target.sandbox.exec(cmd, { raiseOnError: false });
|
|
4196
4240
|
if (result.exitCode !== 0) {
|
|
4197
|
-
const cleanup = await
|
|
4241
|
+
const cleanup = await target.sandbox.exec(`rm -f ${shellQuote(tmp)}`, {
|
|
4198
4242
|
raiseOnError: false
|
|
4199
4243
|
});
|
|
4200
4244
|
if (cleanup.exitCode !== 0) {
|
|
4201
4245
|
const cleanupDetail = typeof cleanup.error === "string" && cleanup.error.trim() || cleanup.output.trim() || `exit ${cleanup.exitCode}`;
|
|
4202
4246
|
console.log(
|
|
4203
|
-
`[ModuleRail] runId=${
|
|
4247
|
+
`[ModuleRail] runId=${target.runId} composition artifacts failed to clean temp ${redactSecrets(tmp)}: ${redactSecrets(cleanupDetail)}`
|
|
4204
4248
|
);
|
|
4205
4249
|
}
|
|
4206
4250
|
const detail = typeof result.error === "string" && result.error.trim() || result.output.trim() || `exit ${result.exitCode}`;
|
|
@@ -4258,6 +4302,40 @@ async function deleteSandboxFile(context, path3) {
|
|
|
4258
4302
|
);
|
|
4259
4303
|
}
|
|
4260
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
|
+
}
|
|
4261
4339
|
async function applyCompositionArtifactMutations(context, plan, existingFiles) {
|
|
4262
4340
|
const appliedWrites = [];
|
|
4263
4341
|
const appliedDeletes = [];
|
|
@@ -4893,7 +4971,7 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
4893
4971
|
context = counted.context;
|
|
4894
4972
|
const deps = context.deps;
|
|
4895
4973
|
if (!deps) throw new Error("Module rail dependencies are required for composition artifacts");
|
|
4896
|
-
const presentRaw = await listPresentModuleNames(context);
|
|
4974
|
+
const presentRaw = await listPresentModuleNames(context.sandbox);
|
|
4897
4975
|
const presentNames = presentRaw.filter((name) => isValidModuleName(name)).sort((a, b) => a.localeCompare(b));
|
|
4898
4976
|
const moduleFacts = [];
|
|
4899
4977
|
const manifestsByName = {};
|
|
@@ -5113,6 +5191,15 @@ async function reconcileCompositionArtifacts(context, options = {}) {
|
|
|
5113
5191
|
console.log(
|
|
5114
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}`
|
|
5115
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
|
+
}
|
|
5116
5203
|
if (changed && !options.dryRun) {
|
|
5117
5204
|
await applyCompositionArtifactMutations(context, planned.plan, existingFiles);
|
|
5118
5205
|
}
|
|
@@ -5130,26 +5217,7 @@ var HOME_I18N_SUBSTRATE_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/substrate`;
|
|
|
5130
5217
|
var HOME_I18N_MESSAGES_DIR = `${APP_PACKAGE_DIR}/src/lib/i18n/messages`;
|
|
5131
5218
|
|
|
5132
5219
|
// src/index.ts
|
|
5133
|
-
var import_zod9 = require("zod");
|
|
5134
5220
|
var MODULES_DIR2 = MODULES_SANDBOX_DIR;
|
|
5135
|
-
var COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
5136
|
-
var composeInputsSchema = import_zod9.z.strictObject({
|
|
5137
|
-
connectedStores: import_zod9.z.array(
|
|
5138
|
-
import_zod9.z.strictObject({
|
|
5139
|
-
storeId: import_zod9.z.string().min(1),
|
|
5140
|
-
slug: import_zod9.z.string().min(1),
|
|
5141
|
-
bindingKey: import_zod9.z.string().min(1).nullish(),
|
|
5142
|
-
accessLevel: import_zod9.z.enum(["read", "write", "admin"])
|
|
5143
|
-
})
|
|
5144
|
-
).default([]),
|
|
5145
|
-
installs: import_zod9.z.record(
|
|
5146
|
-
import_zod9.z.string(),
|
|
5147
|
-
import_zod9.z.strictObject({
|
|
5148
|
-
installedVersion: import_zod9.z.string().min(1),
|
|
5149
|
-
datastoreBindings: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.string()).nullish()
|
|
5150
|
-
})
|
|
5151
|
-
).default({})
|
|
5152
|
-
});
|
|
5153
5221
|
function readComposeInputs(cwd) {
|
|
5154
5222
|
const file = import_node_path2.default.join(cwd, COMPOSE_INPUTS_PATH);
|
|
5155
5223
|
if (!import_node_fs2.default.existsSync(file)) return { connectedStores: [], installs: {} };
|
|
@@ -5183,6 +5251,7 @@ function assertSupportedTypescript(versionMajorMinor) {
|
|
|
5183
5251
|
}
|
|
5184
5252
|
}
|
|
5185
5253
|
var APP_DIR_PREFIX2 = "apps/web/src/app/";
|
|
5254
|
+
var APP_PACKAGE_JSON = "apps/web/package.json";
|
|
5186
5255
|
var STUB_GITIGNORE_PATH = "apps/web/src/app/.gitignore";
|
|
5187
5256
|
var STUB_GITIGNORE_HEADER = "# Generated by stardeck-compose \u2014 Module-rail stubs are build outputs; do not edit or commit them.";
|
|
5188
5257
|
function escapeGitignore(pattern) {
|
|
@@ -5214,10 +5283,10 @@ function assertStubGitignoreOwned(cwd) {
|
|
|
5214
5283
|
}
|
|
5215
5284
|
}
|
|
5216
5285
|
function assertAppRoot(cwd) {
|
|
5217
|
-
const
|
|
5218
|
-
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)) {
|
|
5219
5288
|
throw new Error(
|
|
5220
|
-
`[compose] ${
|
|
5289
|
+
`[compose] ${appPath} not found \u2014 run this from the repo root of a Stardeck app`
|
|
5221
5290
|
);
|
|
5222
5291
|
}
|
|
5223
5292
|
}
|
|
@@ -5321,7 +5390,10 @@ async function compose(options) {
|
|
|
5321
5390
|
assertNoAppLayerImporters(options.cwd, preview.excludedModules);
|
|
5322
5391
|
}
|
|
5323
5392
|
const result = await reconcileCompositionArtifacts(context, {
|
|
5324
|
-
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
|
|
5325
5397
|
});
|
|
5326
5398
|
const gitignoreEntries = options.enabled ? null : writeStubGitignore(options.cwd, result.artifactPaths);
|
|
5327
5399
|
let pruned = 0;
|
package/dist/index.d.mts
CHANGED
|
@@ -8,6 +8,37 @@ type ConnectedStore = {
|
|
|
8
8
|
accessLevel: "read" | "write" | "admin";
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Repo-relative inputs file: the platform's two database reads, as JSON.
|
|
13
|
+
*
|
|
14
|
+
* `@stardeck-customer-apps/compose` runs inside a customer app with no platform
|
|
15
|
+
* database, so the two facts the rail reads from one — the data stores connected
|
|
16
|
+
* to the app and each Module's install row — reach it through this file. The
|
|
17
|
+
* platform writes it (`writeComposeInputs`), compose reads it
|
|
18
|
+
* (`readComposeInputs`); the schema below is the single definition both sides
|
|
19
|
+
* parse against, so a drift on the writing side fails in lib rather than as an
|
|
20
|
+
* unexplained empty datastore binding inside a fork's build.
|
|
21
|
+
*/
|
|
22
|
+
declare const COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
23
|
+
declare const composeInputsSchema: z.ZodObject<{
|
|
24
|
+
connectedStores: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
25
|
+
storeId: z.ZodString;
|
|
26
|
+
slug: z.ZodString;
|
|
27
|
+
bindingKey: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
|
|
28
|
+
accessLevel: z.ZodEnum<{
|
|
29
|
+
read: "read";
|
|
30
|
+
write: "write";
|
|
31
|
+
admin: "admin";
|
|
32
|
+
}>;
|
|
33
|
+
}, z.core.$strict>>>;
|
|
34
|
+
installs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
35
|
+
installedVersion: z.ZodString;
|
|
36
|
+
datastoreBindings: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>>;
|
|
37
|
+
}, z.core.$strict>>>;
|
|
38
|
+
}, z.core.$strict>;
|
|
39
|
+
type ComposeInputsFile = z.infer<typeof composeInputsSchema>;
|
|
40
|
+
type ComposeInputsInstall = ComposeInputsFile["installs"][string];
|
|
41
|
+
|
|
11
42
|
interface ComposeOptions {
|
|
12
43
|
/** Repo root of the customer app (the directory holding `apps/web`). */
|
|
13
44
|
cwd: string;
|
|
@@ -30,28 +61,10 @@ interface ComposeResult {
|
|
|
30
61
|
/** Null when a partial (`--enabled`) compose left the committed list untouched. */
|
|
31
62
|
gitignoreEntries: number | null;
|
|
32
63
|
}
|
|
33
|
-
|
|
34
|
-
declare const COMPOSE_INPUTS_PATH = "apps/web/stardeck.compose.json";
|
|
35
|
-
declare const composeInputsSchema: z.ZodObject<{
|
|
36
|
-
connectedStores: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
37
|
-
storeId: z.ZodString;
|
|
38
|
-
slug: z.ZodString;
|
|
39
|
-
bindingKey: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
-
accessLevel: z.ZodEnum<{
|
|
41
|
-
read: "read";
|
|
42
|
-
write: "write";
|
|
43
|
-
admin: "admin";
|
|
44
|
-
}>;
|
|
45
|
-
}, z.core.$strict>>>;
|
|
46
|
-
installs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
47
|
-
installedVersion: z.ZodString;
|
|
48
|
-
datastoreBindings: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>>;
|
|
49
|
-
}, z.core.$strict>>>;
|
|
50
|
-
}, z.core.$strict>;
|
|
51
|
-
type ComposeInstall = z.infer<typeof composeInputsSchema>["installs"][string];
|
|
64
|
+
|
|
52
65
|
interface ComposeInputs {
|
|
53
66
|
connectedStores: ConnectedStore[];
|
|
54
|
-
installs: Record<string,
|
|
67
|
+
installs: Record<string, ComposeInputsInstall>;
|
|
55
68
|
}
|
|
56
69
|
/**
|
|
57
70
|
* The platform rail resolves data-store bindings and installed versions from
|