create-astrale-domain 0.3.0-beta.4 → 0.3.0-beta.41
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/README.md +12 -1
- package/dist/index.js +94 -101
- package/package.json +19 -14
- package/template/.env.example +5 -1
- package/template/.variants/custom/README.md +7 -6
- package/template/.variants/custom/__tests__/variant.test.ts +13 -0
- package/template/.variants/custom/application.ts +9 -0
- package/template/.variants/custom/schema/application/index.ts +1 -3
- package/template/.variants/custom/ui/application/__tests__/application.test.ts +9 -0
- package/template/.variants/custom/ui/application/index.ts +11 -0
- package/template/.variants/custom/views/application/__tests__/application.view.test.ts +15 -0
- package/template/.variants/custom/views/{app → application/app}/index.html +1 -0
- package/template/.variants/custom/views/application/app/main.ts +3 -0
- package/template/.variants/custom/views/application/app/public/favicon.svg +4 -0
- package/template/.variants/custom/views/application/application.view.ts +7 -0
- package/template/.variants/custom/views/application/routes.ts +4 -5
- package/template/.variants/custom/vite.config.ts +9 -0
- package/template/.variants/none/README.md +2 -1
- package/template/.variants/none/__tests__/variant.test.ts +8 -0
- package/template/.variants/none/application.ts +8 -0
- package/template/.variants/none/schema/application/index.ts +0 -2
- package/template/.variants/react/README.md +13 -12
- package/template/.variants/react/__tests__/variant.test.ts +13 -0
- package/template/.variants/react/application.ts +9 -0
- package/template/.variants/react/frontend/index.html +13 -0
- package/template/.variants/react/frontend/package.json +5 -0
- package/template/.variants/react/frontend/public/favicon.svg +4 -0
- package/template/.variants/react/frontend/src/main.tsx +15 -0
- package/template/.variants/react/frontend/src/routeTree.gen.ts +77 -0
- package/template/.variants/react/frontend/src/router.tsx +11 -0
- package/template/.variants/react/frontend/src/routes/__root.tsx +30 -0
- package/template/.variants/react/frontend/src/routes/about.tsx +16 -0
- package/template/.variants/react/frontend/src/routes/index.tsx +16 -0
- package/template/.variants/react/frontend/src/styles.css +19 -0
- package/template/.variants/react/frontend/src/vite-env.d.ts +1 -0
- package/template/.variants/react/schema/application/index.ts +1 -3
- package/template/.variants/react/ui/application/screen.tsx +6 -6
- package/template/.variants/react/views/application/routes.ts +5 -9
- package/template/.variants/react/vite.config.ts +36 -0
- package/template/CLAUDE.md +10 -5
- package/template/index.ts +2 -1
- package/template/package.json +37 -16
- package/template/pnpm-workspace.yaml +39 -35
- package/template/runtime.ts +13 -0
- package/template/schema/application/__tests__/schema.test.ts +4 -4
- package/template/tests/e2e/application.test.ts +6 -5
- package/template/tsconfig.json +13 -10
- package/template/.variants/custom/implementation.ts +0 -14
- package/template/.variants/custom/views/app/main.ts +0 -10
- package/template/.variants/none/implementation.ts +0 -14
- package/template/.variants/react/implementation.ts +0 -16
- package/template/.variants/react/ui/application/styles.css +0 -34
- package/template/.variants/react/ui/application/styles.d.ts +0 -1
- package/template/.variants/react/views/app/README.md +0 -4
- package/template/.variants/react/views/application/application.view.tsx +0 -19
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Official scaffolder for a new Astrale Domain project.
|
|
4
4
|
|
|
5
|
-
During the SDK beta program, generate a project from the beta
|
|
5
|
+
During the SDK beta program, generate a project from the beta package set explicitly:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npx -y create-astrale-domain@beta my-domain --yes
|
|
@@ -10,3 +10,14 @@ npx -y create-astrale-domain@beta my-domain --yes
|
|
|
10
10
|
|
|
11
11
|
The generated project must resolve the SDK and deployment adapters from npm; it must not retain
|
|
12
12
|
workspace links or repository-only source paths.
|
|
13
|
+
|
|
14
|
+
Generated projects have one package journey:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm pack
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The existing `astrale-domain package` command compiles only the public Schema contract, normalizes
|
|
21
|
+
emitted declarations to semantic SDK facades, and verifies the package boundary. Runtime, handlers,
|
|
22
|
+
Providers, frontend code, and deployment adapters remain implementation inputs and are not part of
|
|
23
|
+
the published Domain contract package.
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
-
|
|
9
|
+
try {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
} catch (e2) {
|
|
12
|
+
throw mod = 0, e2;
|
|
13
|
+
}
|
|
10
14
|
};
|
|
11
15
|
var __copyProps = (to, from, except, desc) => {
|
|
12
16
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -364,7 +368,7 @@ function BD(e2, u2) {
|
|
|
364
368
|
return s;
|
|
365
369
|
}
|
|
366
370
|
var AD = globalThis.process.platform.startsWith("win");
|
|
367
|
-
var S = Symbol("clack:cancel");
|
|
371
|
+
var S = /* @__PURE__ */ Symbol("clack:cancel");
|
|
368
372
|
function pD(e2) {
|
|
369
373
|
return e2 === S;
|
|
370
374
|
}
|
|
@@ -780,6 +784,7 @@ import { existsSync as existsSync2 } from "node:fs";
|
|
|
780
784
|
import { delimiter, join as join2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
781
785
|
|
|
782
786
|
// src/scaffold/index.ts
|
|
787
|
+
import { signingIdentity } from "@astrale-os/adapter-cloudflare/build";
|
|
783
788
|
import { existsSync, readFileSync } from "node:fs";
|
|
784
789
|
import { cp, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
785
790
|
import { createRequire } from "node:module";
|
|
@@ -794,9 +799,7 @@ var INSTANCE_PLACEHOLDER = "my-instance-slug";
|
|
|
794
799
|
var LINK_PACKAGES = /* @__PURE__ */ new Set([
|
|
795
800
|
"@astrale-os/sdk",
|
|
796
801
|
"@astrale-os/adapter-cloudflare",
|
|
797
|
-
"@astrale-os/adapter-astrale"
|
|
798
|
-
"@astrale-os/shell",
|
|
799
|
-
"@astrale-os/shell-react"
|
|
802
|
+
"@astrale-os/adapter-astrale"
|
|
800
803
|
]);
|
|
801
804
|
var ADAPTER_PACKAGES = {
|
|
802
805
|
astrale: "@astrale-os/adapter-astrale",
|
|
@@ -927,8 +930,11 @@ async function writeProject(opts) {
|
|
|
927
930
|
await materializeAdapter(opts);
|
|
928
931
|
await stampOrigin(opts.dir, opts.origin);
|
|
929
932
|
await rewritePackageJson(opts.dir, opts.name, opts.link);
|
|
930
|
-
|
|
931
|
-
|
|
933
|
+
await signingIdentity({
|
|
934
|
+
projectDir: opts.dir,
|
|
935
|
+
origin: opts.origin,
|
|
936
|
+
mode: "local"
|
|
937
|
+
});
|
|
932
938
|
await writeFile(
|
|
933
939
|
join(opts.dir, ".env.dev"),
|
|
934
940
|
"# Dev secrets \u2014 the ENTIRE file is injected into the local runtime by `pnpm dev`.\n# Gitignored. See .env.example.\n"
|
|
@@ -943,42 +949,41 @@ async function materializeGitignores(dir, frontend) {
|
|
|
943
949
|
throw new Error(`Template is missing packaged Git ignore file ${packagedPath}.`);
|
|
944
950
|
}
|
|
945
951
|
}
|
|
946
|
-
|
|
947
|
-
const path = join(dir, ".gitignore");
|
|
948
|
-
const current = await readFile(path, "utf-8");
|
|
949
|
-
await writeFile(
|
|
950
|
-
path,
|
|
951
|
-
`${current.trimEnd()}
|
|
952
|
-
|
|
953
|
-
# SDK-managed React bootstrap (regenerated before every frontend build)
|
|
954
|
-
/views/app/index.html
|
|
955
|
-
/views/app/astrale.generated.tsx
|
|
956
|
-
`
|
|
957
|
-
);
|
|
958
|
-
}
|
|
952
|
+
void frontend;
|
|
959
953
|
}
|
|
960
954
|
async function materializeFrontend(opts, template) {
|
|
961
955
|
const variant = join(template, ".variants", opts.frontend);
|
|
962
956
|
if (!existsSync(variant)) throw new Error(`Frontend template not found at ${variant}.`);
|
|
963
|
-
await cp(variant, opts.dir, {
|
|
957
|
+
await cp(variant, opts.dir, {
|
|
958
|
+
recursive: true,
|
|
959
|
+
filter: (source) => relative(variant, source) !== "implementation.ts"
|
|
960
|
+
});
|
|
964
961
|
const path = join(opts.dir, "package.json");
|
|
965
962
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
966
963
|
pkg.dependencies ??= {};
|
|
967
964
|
pkg.devDependencies ??= {};
|
|
965
|
+
pkg.scripts ??= {};
|
|
968
966
|
if (opts.frontend === "react") {
|
|
969
|
-
Object.assign(pkg.
|
|
970
|
-
"@
|
|
971
|
-
"@astrale-os/shell-react": ">=0.2.10-beta.2 <1.0.0",
|
|
967
|
+
Object.assign(pkg.devDependencies, {
|
|
968
|
+
"@tanstack/react-router": "1.170.17",
|
|
972
969
|
react: "^19.2.0",
|
|
973
970
|
"react-dom": "^19.2.0"
|
|
974
971
|
});
|
|
975
972
|
Object.assign(pkg.devDependencies, {
|
|
973
|
+
"@tailwindcss/vite": "^4.0.6",
|
|
974
|
+
"@tanstack/router-plugin": "1.168.19",
|
|
976
975
|
"@types/react": "^19.2.0",
|
|
977
976
|
"@types/react-dom": "^19.2.0",
|
|
978
|
-
|
|
977
|
+
"@vitejs/plugin-react": "^6.0.5",
|
|
978
|
+
tailwindcss: "^4.0.6",
|
|
979
|
+
vite: "^8.2.1"
|
|
979
980
|
});
|
|
980
981
|
} else if (opts.frontend === "custom") {
|
|
981
|
-
pkg.devDependencies.vite = "^
|
|
982
|
+
pkg.devDependencies.vite = "^8.2.1";
|
|
983
|
+
}
|
|
984
|
+
if (opts.frontend !== "none") {
|
|
985
|
+
pkg.scripts["frontend:dev"] = "vite";
|
|
986
|
+
pkg.scripts["frontend:build"] = "vite build";
|
|
982
987
|
}
|
|
983
988
|
pkg.dependencies = sorted(pkg.dependencies);
|
|
984
989
|
pkg.devDependencies = sorted(pkg.devDependencies);
|
|
@@ -1007,23 +1012,18 @@ async function stampOrigin(dir, origin) {
|
|
|
1007
1012
|
async function materializeAdapter(opts) {
|
|
1008
1013
|
const config = join(opts.dir, "astrale.config.ts");
|
|
1009
1014
|
await cp(join(templateDir(ADAPTER_PACKAGES[opts.adapter]), "astrale.config.ts"), config);
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1015
|
+
await addAdapterDependency(opts.dir, ADAPTER_PACKAGES[opts.adapter]);
|
|
1016
|
+
if (opts.adapter === "astrale" && opts.instance) {
|
|
1017
|
+
await replaceInFile(config, `'${INSTANCE_PLACEHOLDER}'`, `'${opts.instance}'`);
|
|
1018
|
+
}
|
|
1013
1019
|
}
|
|
1014
|
-
async function
|
|
1020
|
+
async function addAdapterDependency(dir, adapter) {
|
|
1015
1021
|
const path = join(dir, "package.json");
|
|
1016
1022
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
)
|
|
1021
|
-
}
|
|
1022
|
-
pkg.dependencies["@astrale-os/adapter-astrale"] = installedPackageRange(
|
|
1023
|
-
"@astrale-os/adapter-astrale"
|
|
1024
|
-
);
|
|
1025
|
-
pkg.dependencies = Object.fromEntries(
|
|
1026
|
-
Object.entries(pkg.dependencies).sort(([a], [b3]) => a.localeCompare(b3))
|
|
1023
|
+
pkg.devDependencies ??= {};
|
|
1024
|
+
pkg.devDependencies[adapter] = installedPackageRange(adapter);
|
|
1025
|
+
pkg.devDependencies = Object.fromEntries(
|
|
1026
|
+
Object.entries(pkg.devDependencies).sort(([a], [b3]) => a.localeCompare(b3))
|
|
1027
1027
|
);
|
|
1028
1028
|
await writeFile(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
1029
1029
|
}
|
|
@@ -1031,9 +1031,25 @@ async function rewritePackageJson(dir, name, link) {
|
|
|
1031
1031
|
const path = join(dir, "package.json");
|
|
1032
1032
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
1033
1033
|
pkg.name = name;
|
|
1034
|
+
if (pkg.imports !== void 0) {
|
|
1035
|
+
pkg.publishConfig ??= {};
|
|
1036
|
+
pkg.publishConfig.imports = publishedImports(pkg.imports);
|
|
1037
|
+
}
|
|
1034
1038
|
rewriteAstraleDeps(pkg, link);
|
|
1035
1039
|
await writeFile(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
1036
1040
|
}
|
|
1041
|
+
function publishedImports(imports) {
|
|
1042
|
+
return Object.fromEntries(
|
|
1043
|
+
Object.entries(imports).filter(([specifier]) => specifier === "#schema" || specifier === "#schema/*").map(([specifier, target]) => {
|
|
1044
|
+
if (!/^\.\/.*\.ts$/u.test(target)) {
|
|
1045
|
+
throw new TypeError(
|
|
1046
|
+
`Package import ${specifier} must target one project TypeScript file.`
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
return [specifier, `./dist/${target.slice(2, -3)}.js`];
|
|
1050
|
+
})
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1037
1053
|
function rewriteAstraleDeps(pkg, link) {
|
|
1038
1054
|
if (!link) return false;
|
|
1039
1055
|
let changed = false;
|
|
@@ -1050,46 +1066,6 @@ function rewriteAstraleDeps(pkg, link) {
|
|
|
1050
1066
|
}
|
|
1051
1067
|
return changed;
|
|
1052
1068
|
}
|
|
1053
|
-
var FIRST_PARTY_SCOPES = ["@astrale-domains/*", "@astrale-os/*"];
|
|
1054
|
-
async function writeLinkedZodOverride(dir) {
|
|
1055
|
-
const workspace = join(dir, "pnpm-workspace.yaml");
|
|
1056
|
-
if (!existsSync(workspace)) return;
|
|
1057
|
-
const start = "# create-astrale-domain: linked overrides";
|
|
1058
|
-
const end = "# /create-astrale-domain";
|
|
1059
|
-
const block = `
|
|
1060
|
-
${start}
|
|
1061
|
-
overrides:
|
|
1062
|
-
zod: ${sdkZodVersion()}
|
|
1063
|
-
${end}
|
|
1064
|
-
`;
|
|
1065
|
-
const text = await readFile(workspace, "utf-8");
|
|
1066
|
-
const pattern = new RegExp(`\\n?${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\n?`);
|
|
1067
|
-
await writeFile(workspace, text.replace(pattern, "\n").trimEnd() + block);
|
|
1068
|
-
}
|
|
1069
|
-
async function writeReleaseAgePolicy(dir) {
|
|
1070
|
-
const workspace = join(dir, "pnpm-workspace.yaml");
|
|
1071
|
-
if (!existsSync(workspace)) return;
|
|
1072
|
-
const start = "# create-astrale-domain: first-party scopes";
|
|
1073
|
-
const end = "# /create-astrale-domain";
|
|
1074
|
-
const block = `
|
|
1075
|
-
${start}
|
|
1076
|
-
minimumReleaseAgeExclude:
|
|
1077
|
-
` + FIRST_PARTY_SCOPES.map((scope) => ` - '${scope}'`).join("\n") + `
|
|
1078
|
-
${end}
|
|
1079
|
-
`;
|
|
1080
|
-
const text = await readFile(workspace, "utf-8");
|
|
1081
|
-
const pattern = new RegExp(`\\n?${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\n?`);
|
|
1082
|
-
await writeFile(workspace, text.replace(pattern, "\n").trimEnd() + block);
|
|
1083
|
-
}
|
|
1084
|
-
function escapeRegExp(value) {
|
|
1085
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1086
|
-
}
|
|
1087
|
-
function sdkZodVersion() {
|
|
1088
|
-
if (SDK_ROOT === void 0) throw new Error("Local SDK checkout is unavailable.");
|
|
1089
|
-
const sdkRequire = createRequire(join(SDK_ROOT, "package.json"));
|
|
1090
|
-
const pkg = sdkRequire("zod/package.json");
|
|
1091
|
-
return pkg.version;
|
|
1092
|
-
}
|
|
1093
1069
|
async function replaceInFile(path, from, to) {
|
|
1094
1070
|
if (!existsSync(path)) return;
|
|
1095
1071
|
const text = await readFile(path, "utf-8");
|
|
@@ -1115,13 +1091,6 @@ function localPackageRoot(dependency) {
|
|
|
1115
1091
|
const installed = dirname(resolved.path);
|
|
1116
1092
|
if (!installed.split(sep).includes("node_modules")) return installed;
|
|
1117
1093
|
}
|
|
1118
|
-
if ((dependency === "@astrale-os/shell" || dependency === "@astrale-os/shell-react") && SDK_ROOT !== void 0) {
|
|
1119
|
-
const packageDirectory = dependency === "@astrale-os/shell" ? "shell" : "shell-react";
|
|
1120
|
-
for (const base of [dirname(SDK_ROOT), dirname(dirname(SDK_ROOT))]) {
|
|
1121
|
-
const candidate = join(base, "shell", "packages", packageDirectory);
|
|
1122
|
-
if (existsSync(join(candidate, "package.json"))) return candidate;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
1094
|
throw new Error(`No active source package is available for ${dependency}.`);
|
|
1126
1095
|
}
|
|
1127
1096
|
|
|
@@ -1175,6 +1144,10 @@ async function main() {
|
|
|
1175
1144
|
const headless = flags.yes || !process.stdin.isTTY;
|
|
1176
1145
|
Ie("Astrale \u2014 new domain");
|
|
1177
1146
|
let slug = flags.positional[0];
|
|
1147
|
+
if (flags.positional.length > 1) {
|
|
1148
|
+
xe(`Unexpected positional argument "${flags.positional[1]}".`);
|
|
1149
|
+
return 1;
|
|
1150
|
+
}
|
|
1178
1151
|
if (!slug) {
|
|
1179
1152
|
if (headless) {
|
|
1180
1153
|
xe("A domain slug is required in non-interactive mode (pass it as the first argument).");
|
|
@@ -1249,6 +1222,10 @@ async function main() {
|
|
|
1249
1222
|
pm = answer;
|
|
1250
1223
|
}
|
|
1251
1224
|
}
|
|
1225
|
+
if (!PMS.includes(pm)) {
|
|
1226
|
+
xe(`Unknown package manager "${pm}" \u2014 use ${PMS.join(", ")}.`);
|
|
1227
|
+
return 1;
|
|
1228
|
+
}
|
|
1252
1229
|
const dir = resolve2(flags.dir ?? slug);
|
|
1253
1230
|
if (existsSync2(dir) && !flags.force) {
|
|
1254
1231
|
if (headless) {
|
|
@@ -1259,8 +1236,10 @@ async function main() {
|
|
|
1259
1236
|
if (pD(go) || !go) return bail();
|
|
1260
1237
|
}
|
|
1261
1238
|
const link = flags.link ?? workspaceAvailable();
|
|
1262
|
-
if (flags.origin !== void 0 && !
|
|
1263
|
-
xe(
|
|
1239
|
+
if (flags.origin !== void 0 && !isValidOrigin(flags.origin)) {
|
|
1240
|
+
xe(
|
|
1241
|
+
`Invalid --origin "${flags.origin}". Use a lowercase dotted domain with valid DNS labels.`
|
|
1242
|
+
);
|
|
1264
1243
|
return 1;
|
|
1265
1244
|
}
|
|
1266
1245
|
const origin = flags.origin ?? (slug.includes(".") ? slug : `${slug}.example.dev`);
|
|
@@ -1293,13 +1272,13 @@ async function main() {
|
|
|
1293
1272
|
`${pm} install`,
|
|
1294
1273
|
devStep(pm),
|
|
1295
1274
|
installStep(pm),
|
|
1296
|
-
|
|
1275
|
+
deployStep(pm, adapter, instance)
|
|
1297
1276
|
].join("\n"),
|
|
1298
1277
|
"Next"
|
|
1299
1278
|
);
|
|
1300
1279
|
if (adapter === "astrale" && !instance) {
|
|
1301
1280
|
M2.warn(
|
|
1302
|
-
`No target instance set \u2014 put your slug in astrale.config.ts (prod.instance) before \`${pm
|
|
1281
|
+
`No target instance set \u2014 put your slug in astrale.config.ts (prod.instance) before \`${deployCommand(pm)}\`. Create one: astrale instance create <slug>`
|
|
1303
1282
|
);
|
|
1304
1283
|
}
|
|
1305
1284
|
M2.info(
|
|
@@ -1309,11 +1288,11 @@ async function main() {
|
|
|
1309
1288
|
return 0;
|
|
1310
1289
|
}
|
|
1311
1290
|
var devStep = (pm) => `${pm === "npm" ? "npm run dev" : `${pm} dev`} # wrangler dev \u2192 prints a local URL`;
|
|
1312
|
-
var
|
|
1313
|
-
const
|
|
1314
|
-
|
|
1315
|
-
return `${cmd} ${hint}`;
|
|
1291
|
+
var deployStep = (pm, adapter, instance) => {
|
|
1292
|
+
const hint = adapter === "astrale" ? `# managed deploy \u2192 installs on ${instance ? `"${instance}"` : "your instance"}` : "# deploy production \u2192 prints a URL";
|
|
1293
|
+
return `${deployCommand(pm)} ${hint}`;
|
|
1316
1294
|
};
|
|
1295
|
+
var deployCommand = (pm) => pm === "npm" ? "npm run deploy -- prod" : `${pm} deploy prod`;
|
|
1317
1296
|
function installStep(pm) {
|
|
1318
1297
|
if (astraleOnPath()) return "astrale domain install <url> --direct # mount it on an instance";
|
|
1319
1298
|
return [
|
|
@@ -1341,16 +1320,24 @@ function parseFlags(argv) {
|
|
|
1341
1320
|
else if (a === "--force") flags.force = true;
|
|
1342
1321
|
else if (a === "--link") flags.link = true;
|
|
1343
1322
|
else if (a === "--no-link") flags.link = false;
|
|
1344
|
-
else if (a === "--adapter") flags.adapter = argv
|
|
1345
|
-
else if (a === "--frontend") flags.frontend = argv
|
|
1346
|
-
else if (a === "--pm") flags.pm = argv
|
|
1347
|
-
else if (a === "--dir") flags.dir = argv
|
|
1348
|
-
else if (a === "--origin") flags.origin = argv
|
|
1349
|
-
else if (a === "--instance") flags.instance = argv
|
|
1323
|
+
else if (a === "--adapter") flags.adapter = flagValue(argv, ++i, a);
|
|
1324
|
+
else if (a === "--frontend") flags.frontend = flagValue(argv, ++i, a);
|
|
1325
|
+
else if (a === "--pm") flags.pm = flagValue(argv, ++i, a);
|
|
1326
|
+
else if (a === "--dir") flags.dir = flagValue(argv, ++i, a);
|
|
1327
|
+
else if (a === "--origin") flags.origin = flagValue(argv, ++i, a);
|
|
1328
|
+
else if (a === "--instance") flags.instance = flagValue(argv, ++i, a);
|
|
1329
|
+
else if (a.startsWith("-")) throw new TypeError(`Unknown option "${a}".`);
|
|
1350
1330
|
else if (!a.startsWith("-")) flags.positional.push(a);
|
|
1351
1331
|
}
|
|
1352
1332
|
return flags;
|
|
1353
1333
|
}
|
|
1334
|
+
function flagValue(argv, index, flag) {
|
|
1335
|
+
const value = argv[index];
|
|
1336
|
+
if (value === void 0 || value.startsWith("-")) {
|
|
1337
|
+
throw new TypeError(`${flag} requires a value.`);
|
|
1338
|
+
}
|
|
1339
|
+
return value;
|
|
1340
|
+
}
|
|
1354
1341
|
function printHelp() {
|
|
1355
1342
|
process.stdout.write(
|
|
1356
1343
|
`
|
|
@@ -1384,6 +1371,12 @@ Non-interactive (also auto-enabled without a TTY, e.g. CI):
|
|
|
1384
1371
|
function isValidSlug(v2) {
|
|
1385
1372
|
return /^[a-z][a-z0-9.-]*$/.test(v2);
|
|
1386
1373
|
}
|
|
1374
|
+
function isValidOrigin(value) {
|
|
1375
|
+
if (value.length > 253 || !value.includes(".")) return false;
|
|
1376
|
+
return value.split(".").every(
|
|
1377
|
+
(label) => label.length > 0 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label)
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1387
1380
|
function bail() {
|
|
1388
1381
|
xe("Cancelled.");
|
|
1389
1382
|
return 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-astrale-domain",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.41",
|
|
4
4
|
"description": "Scaffold a standalone Astrale domain — npm create astrale-domain",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astrale",
|
|
@@ -10,38 +10,43 @@
|
|
|
10
10
|
],
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
12
|
"author": "Astrale",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/astrale-os/sdk.git"
|
|
16
|
+
},
|
|
13
17
|
"bin": {
|
|
14
18
|
"create-astrale-domain": "./dist/index.js"
|
|
15
19
|
},
|
|
16
20
|
"files": [
|
|
17
21
|
"dist",
|
|
22
|
+
"!dist/**/*.map",
|
|
18
23
|
"template",
|
|
19
|
-
"!template
|
|
20
|
-
"!template
|
|
24
|
+
"!template/**/node_modules",
|
|
25
|
+
"!template/**/node_modules/**",
|
|
26
|
+
"!template/**/pnpm-lock.yaml"
|
|
21
27
|
],
|
|
22
28
|
"type": "module",
|
|
23
29
|
"dependencies": {
|
|
24
30
|
"@clack/prompts": "^0.11.0",
|
|
25
|
-
"@astrale-os/adapter-astrale": "^0.5.0-beta.
|
|
26
|
-
"@astrale-os/adapter-cloudflare": "^0.5.0-beta.
|
|
31
|
+
"@astrale-os/adapter-astrale": "^0.5.0-beta.41",
|
|
32
|
+
"@astrale-os/adapter-cloudflare": "^0.5.0-beta.48"
|
|
27
33
|
},
|
|
28
34
|
"devDependencies": {
|
|
29
35
|
"@astrale-os/ox": ">=0.1.0 <1.0.0",
|
|
30
36
|
"@types/node": "^22.0.0",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"@astrale-os/sdk": "^0.5.0-beta.8"
|
|
37
|
+
"esbuild": "^0.28.2",
|
|
38
|
+
"oxfmt": "0.63.0",
|
|
39
|
+
"oxlint": "1.78.0",
|
|
40
|
+
"typescript": "7.0.2",
|
|
41
|
+
"vitest": "^4.1.10",
|
|
42
|
+
"@astrale-os/sdk": "^0.5.0-beta.45"
|
|
38
43
|
},
|
|
39
44
|
"engines": {
|
|
40
45
|
"node": ">=22"
|
|
41
46
|
},
|
|
42
47
|
"scripts": {
|
|
43
|
-
"build": "node scripts/build.mjs",
|
|
44
|
-
"typecheck": "
|
|
48
|
+
"build": "rm -rf dist && node scripts/build.mjs && node ../scripts/verify-no-source-maps.mjs dist",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
45
50
|
"test": "vitest run",
|
|
46
51
|
"test:generated": "node scripts/verify-generated.mjs",
|
|
47
52
|
"lint": "oxlint .",
|
package/template/.env.example
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
# prod. These files are gitignored — copy this to `.env.dev` and fill in.
|
|
5
5
|
#
|
|
6
6
|
# Add one line per secret required by an Integration. Declare the corresponding
|
|
7
|
-
# binding
|
|
7
|
+
# binding returned by `runtime.ts` initialization. For example:
|
|
8
8
|
#
|
|
9
9
|
# EXAMPLE_API_KEY=sk-...
|
|
10
|
+
|
|
11
|
+
# The Cloudflare adapter's execution identity is separate from Integration
|
|
12
|
+
# secrets. The scaffold creates `.astrale/identity.json`; retain and restore
|
|
13
|
+
# that file for production rather than copying its value into this dotenv file.
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# Astrale Domain
|
|
2
2
|
|
|
3
|
-
This project uses the framework-neutral frontend escape hatch. `views/routes.ts` binds
|
|
4
|
-
Views to
|
|
3
|
+
This project uses the framework-neutral frontend escape hatch. `views/application/routes.ts` binds
|
|
4
|
+
declared Views to `defineFrontend`; `vite.config.ts` owns the ordinary Vite root you can replace with any
|
|
5
5
|
framework; `ui/` owns presentation assets. The starter route uses `handshake: 'none'`, so it mounts
|
|
6
6
|
as a plain hosted app. Change that route to `handshake: 'shell'` only after your browser entrypoint
|
|
7
7
|
boots the Shell protocol.
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
10
|
schema/ public Domain contract, including View declarations
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
views/
|
|
14
|
-
|
|
11
|
+
runtime.ts Actions, Workflows, Integrations, Providers, and initialization
|
|
12
|
+
application.ts Schema, Runtime, and frontend composition
|
|
13
|
+
views/ framework-neutral frontend declaration
|
|
14
|
+
views/application/ application View, route, and browser entrypoint
|
|
15
|
+
ui/application/ application presentation, tokens, and styles
|
|
15
16
|
tests/ cross-layer executable journeys
|
|
16
17
|
```
|
|
17
18
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { application } from '../application.js'
|
|
2
|
+
|
|
3
|
+
describe('custom frontend variant', () => {
|
|
4
|
+
/** @evidence SDK-V1-SCAFFOLD-CUSTOM */
|
|
5
|
+
it('rejects missing authored frontend composition', () => {
|
|
6
|
+
expect(application.frontend).toBeDefined()
|
|
7
|
+
expect(application.frontend?.source.kind).toBe('vite')
|
|
8
|
+
expect(application.frontend?.routes.application).toMatchObject({
|
|
9
|
+
path: '/application',
|
|
10
|
+
handshake: 'none',
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineApplication } from '@astrale-os/sdk/application'
|
|
2
|
+
|
|
3
|
+
import { schema } from '#schema'
|
|
4
|
+
import { frontend } from '#views'
|
|
5
|
+
|
|
6
|
+
import runtime from './runtime.js'
|
|
7
|
+
|
|
8
|
+
/** Build and publication composition; provider effects live in astrale.config.ts. */
|
|
9
|
+
export const application = defineApplication({ schema, runtime, frontend })
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { defineSchema, view } from '@astrale-os/sdk/schema'
|
|
2
2
|
|
|
3
3
|
export const schema = defineSchema('astrale-domain.example.dev', {
|
|
4
|
-
interfaces: {},
|
|
5
4
|
classes: {},
|
|
6
|
-
functions: {},
|
|
7
5
|
views: {
|
|
8
|
-
application: view({
|
|
6
|
+
application: view({}),
|
|
9
7
|
},
|
|
10
8
|
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { applicationMarkup, applicationTheme } from '../index.js'
|
|
2
|
+
|
|
3
|
+
describe('application UI', () => {
|
|
4
|
+
it('owns the framework-neutral presentation', () => {
|
|
5
|
+
expect(applicationTheme.accent).toBe('#e3b866')
|
|
6
|
+
expect(applicationMarkup()).toContain('Custom frontend')
|
|
7
|
+
expect(applicationMarkup()).toContain('Ready to build.')
|
|
8
|
+
})
|
|
9
|
+
})
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
+
import './styles.css'
|
|
2
|
+
|
|
1
3
|
/** Framework-neutral presentation tokens and styles for the application. */
|
|
2
4
|
export const applicationTheme = Object.freeze({ accent: '#e3b866' })
|
|
5
|
+
|
|
6
|
+
/** Pure presentation owned by the application UI feature. */
|
|
7
|
+
export function applicationMarkup(): string {
|
|
8
|
+
return `
|
|
9
|
+
<p class="application-eyebrow">Custom frontend</p>
|
|
10
|
+
<h1>Ready to build.</h1>
|
|
11
|
+
<p>Replace the application browser entrypoint while preserving its View route.</p>
|
|
12
|
+
`
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { mountApplication } from '../application.view.js'
|
|
2
|
+
|
|
3
|
+
describe('application View', () => {
|
|
4
|
+
it('mounts the UI owned by the application feature', () => {
|
|
5
|
+
const application = { innerHTML: '' } as HTMLElement
|
|
6
|
+
|
|
7
|
+
mountApplication(application)
|
|
8
|
+
|
|
9
|
+
expect(application.innerHTML).toContain('Ready to build.')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('rejects a missing browser root', () => {
|
|
13
|
+
expect(() => mountApplication(null)).toThrow('Custom frontend root #application is missing.')
|
|
14
|
+
})
|
|
15
|
+
})
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { applicationMarkup } from '#ui/application'
|
|
2
|
+
|
|
3
|
+
/** Mount the application presentation into its browser-owned root. */
|
|
4
|
+
export function mountApplication(application: HTMLElement | null): void {
|
|
5
|
+
if (application === null) throw new Error('Custom frontend root #application is missing.')
|
|
6
|
+
application.innerHTML = applicationMarkup()
|
|
7
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineFrontend, vite } from '@astrale-os/sdk/view'
|
|
2
2
|
|
|
3
3
|
import { schema } from '#schema'
|
|
4
4
|
|
|
5
5
|
/** Framework-neutral escape hatch: swap Vite or the browser implementation without changing Views. */
|
|
6
|
-
export const frontend =
|
|
6
|
+
export const frontend = defineFrontend({
|
|
7
7
|
schema,
|
|
8
|
-
source:
|
|
8
|
+
source: vite(),
|
|
9
9
|
routes: {
|
|
10
|
-
application:
|
|
10
|
+
application: { handshake: 'none' },
|
|
11
11
|
},
|
|
12
|
-
entrypoint: 'application',
|
|
13
12
|
})
|
|
@@ -4,7 +4,8 @@ This is a callable-only Domain with no frontend artifact or browser assets.
|
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
schema/ public Domain contract
|
|
7
|
-
|
|
7
|
+
runtime.ts Actions, Workflows, Integrations, Providers, and initialization
|
|
8
|
+
application.ts Schema and Runtime composition
|
|
8
9
|
tests/ cross-layer executable journeys
|
|
9
10
|
```
|
|
10
11
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { application } from '../application.js'
|
|
2
|
+
|
|
3
|
+
describe('none frontend variant', () => {
|
|
4
|
+
/** @evidence SDK-V1-SCAFFOLD-NONE */
|
|
5
|
+
it('rejects an implicit browser frontend by keeping Application headless', () => {
|
|
6
|
+
expect(application.frontend).toBeUndefined()
|
|
7
|
+
})
|
|
8
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineApplication } from '@astrale-os/sdk/application'
|
|
2
|
+
|
|
3
|
+
import { schema } from '#schema'
|
|
4
|
+
|
|
5
|
+
import runtime from './runtime.js'
|
|
6
|
+
|
|
7
|
+
/** Build and publication composition; provider effects live in astrale.config.ts. */
|
|
8
|
+
export const application = defineApplication({ schema, runtime })
|