create-astrale-domain 0.3.0-beta.2 → 0.3.0-beta.20
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 +11 -1
- package/dist/index.js +80 -59
- 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/application/app/main.ts +3 -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 +12 -11
- 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 +12 -0
- package/template/.variants/react/frontend/package.json +5 -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 +9 -5
- package/template/index.ts +2 -1
- package/template/package.json +45 -15
- package/template/pnpm-workspace.yaml +31 -25
- 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/tsconfig.package.json +29 -0
- 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/template/.variants/custom/views/{app → application/app}/index.html +0 -0
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,13 @@ 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 the public Schema and independently loadable
|
|
21
|
+
Runtime, normalizes emitted declarations to semantic SDK facades, and verifies the archive boundary.
|
|
22
|
+
Deployment adapters remain development dependencies and are not part of the published Domain runtime.
|
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",
|
|
@@ -928,7 +931,11 @@ async function writeProject(opts) {
|
|
|
928
931
|
await stampOrigin(opts.dir, opts.origin);
|
|
929
932
|
await rewritePackageJson(opts.dir, opts.name, opts.link);
|
|
930
933
|
if (opts.link) await writeLinkedZodOverride(opts.dir);
|
|
931
|
-
|
|
934
|
+
await signingIdentity({
|
|
935
|
+
projectDir: opts.dir,
|
|
936
|
+
origin: opts.origin,
|
|
937
|
+
mode: "local"
|
|
938
|
+
});
|
|
932
939
|
await writeFile(
|
|
933
940
|
join(opts.dir, ".env.dev"),
|
|
934
941
|
"# Dev secrets \u2014 the ENTIRE file is injected into the local runtime by `pnpm dev`.\n# Gitignored. See .env.example.\n"
|
|
@@ -943,42 +950,41 @@ async function materializeGitignores(dir, frontend) {
|
|
|
943
950
|
throw new Error(`Template is missing packaged Git ignore file ${packagedPath}.`);
|
|
944
951
|
}
|
|
945
952
|
}
|
|
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
|
-
}
|
|
953
|
+
void frontend;
|
|
959
954
|
}
|
|
960
955
|
async function materializeFrontend(opts, template) {
|
|
961
956
|
const variant = join(template, ".variants", opts.frontend);
|
|
962
957
|
if (!existsSync(variant)) throw new Error(`Frontend template not found at ${variant}.`);
|
|
963
|
-
await cp(variant, opts.dir, {
|
|
958
|
+
await cp(variant, opts.dir, {
|
|
959
|
+
recursive: true,
|
|
960
|
+
filter: (source) => relative(variant, source) !== "implementation.ts"
|
|
961
|
+
});
|
|
964
962
|
const path = join(opts.dir, "package.json");
|
|
965
963
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
966
964
|
pkg.dependencies ??= {};
|
|
967
965
|
pkg.devDependencies ??= {};
|
|
966
|
+
pkg.scripts ??= {};
|
|
968
967
|
if (opts.frontend === "react") {
|
|
969
968
|
Object.assign(pkg.dependencies, {
|
|
970
|
-
"@
|
|
971
|
-
"@astrale-os/shell-react": ">=0.2.10-beta.2 <1.0.0",
|
|
969
|
+
"@tanstack/react-router": "1.170.17",
|
|
972
970
|
react: "^19.2.0",
|
|
973
971
|
"react-dom": "^19.2.0"
|
|
974
972
|
});
|
|
975
973
|
Object.assign(pkg.devDependencies, {
|
|
974
|
+
"@tailwindcss/vite": "^4.0.6",
|
|
975
|
+
"@tanstack/router-plugin": "1.168.19",
|
|
976
976
|
"@types/react": "^19.2.0",
|
|
977
977
|
"@types/react-dom": "^19.2.0",
|
|
978
|
-
|
|
978
|
+
"@vitejs/plugin-react": "^6.0.5",
|
|
979
|
+
tailwindcss: "^4.0.6",
|
|
980
|
+
vite: "^8.2.1"
|
|
979
981
|
});
|
|
980
982
|
} else if (opts.frontend === "custom") {
|
|
981
|
-
pkg.devDependencies.vite = "^
|
|
983
|
+
pkg.devDependencies.vite = "^8.2.1";
|
|
984
|
+
}
|
|
985
|
+
if (opts.frontend !== "none") {
|
|
986
|
+
pkg.scripts["frontend:dev"] = "vite";
|
|
987
|
+
pkg.scripts["frontend:build"] = "vite build";
|
|
982
988
|
}
|
|
983
989
|
pkg.dependencies = sorted(pkg.dependencies);
|
|
984
990
|
pkg.devDependencies = sorted(pkg.devDependencies);
|
|
@@ -1014,16 +1020,16 @@ async function materializeAdapter(opts) {
|
|
|
1014
1020
|
async function addManagedAdapterDep(dir) {
|
|
1015
1021
|
const path = join(dir, "package.json");
|
|
1016
1022
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
1017
|
-
if (!pkg.
|
|
1023
|
+
if (!pkg.devDependencies || !("@astrale-os/adapter-cloudflare" in pkg.devDependencies)) {
|
|
1018
1024
|
throw new Error(
|
|
1019
1025
|
"Could not materialize @astrale-os/adapter-astrale: the scaffold body does not declare its @astrale-os/adapter-cloudflare Worker runtime dependency."
|
|
1020
1026
|
);
|
|
1021
1027
|
}
|
|
1022
|
-
pkg.
|
|
1028
|
+
pkg.devDependencies["@astrale-os/adapter-astrale"] = installedPackageRange(
|
|
1023
1029
|
"@astrale-os/adapter-astrale"
|
|
1024
1030
|
);
|
|
1025
|
-
pkg.
|
|
1026
|
-
Object.entries(pkg.
|
|
1031
|
+
pkg.devDependencies = Object.fromEntries(
|
|
1032
|
+
Object.entries(pkg.devDependencies).sort(([a], [b3]) => a.localeCompare(b3))
|
|
1027
1033
|
);
|
|
1028
1034
|
await writeFile(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
1029
1035
|
}
|
|
@@ -1031,9 +1037,23 @@ async function rewritePackageJson(dir, name, link) {
|
|
|
1031
1037
|
const path = join(dir, "package.json");
|
|
1032
1038
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
1033
1039
|
pkg.name = name;
|
|
1040
|
+
if (pkg.imports !== void 0) {
|
|
1041
|
+
pkg.publishConfig ??= {};
|
|
1042
|
+
pkg.publishConfig.imports = publishedImports(pkg.imports);
|
|
1043
|
+
}
|
|
1034
1044
|
rewriteAstraleDeps(pkg, link);
|
|
1035
1045
|
await writeFile(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
1036
1046
|
}
|
|
1047
|
+
function publishedImports(imports) {
|
|
1048
|
+
return Object.fromEntries(
|
|
1049
|
+
Object.entries(imports).map(([specifier, target]) => {
|
|
1050
|
+
if (!/^\.\/.*\.ts$/u.test(target)) {
|
|
1051
|
+
throw new TypeError(`Package import ${specifier} must target one project TypeScript file.`);
|
|
1052
|
+
}
|
|
1053
|
+
return [specifier, `./dist/${target.slice(2, -3)}.js`];
|
|
1054
|
+
})
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1037
1057
|
function rewriteAstraleDeps(pkg, link) {
|
|
1038
1058
|
if (!link) return false;
|
|
1039
1059
|
let changed = false;
|
|
@@ -1050,7 +1070,6 @@ function rewriteAstraleDeps(pkg, link) {
|
|
|
1050
1070
|
}
|
|
1051
1071
|
return changed;
|
|
1052
1072
|
}
|
|
1053
|
-
var FIRST_PARTY_SCOPES = ["@astrale-domains/*", "@astrale-os/*"];
|
|
1054
1073
|
async function writeLinkedZodOverride(dir) {
|
|
1055
1074
|
const workspace = join(dir, "pnpm-workspace.yaml");
|
|
1056
1075
|
if (!existsSync(workspace)) return;
|
|
@@ -1066,21 +1085,6 @@ ${end}
|
|
|
1066
1085
|
const pattern = new RegExp(`\\n?${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\n?`);
|
|
1067
1086
|
await writeFile(workspace, text.replace(pattern, "\n").trimEnd() + block);
|
|
1068
1087
|
}
|
|
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
1088
|
function escapeRegExp(value) {
|
|
1085
1089
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1086
1090
|
}
|
|
@@ -1115,13 +1119,6 @@ function localPackageRoot(dependency) {
|
|
|
1115
1119
|
const installed = dirname(resolved.path);
|
|
1116
1120
|
if (!installed.split(sep).includes("node_modules")) return installed;
|
|
1117
1121
|
}
|
|
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
1122
|
throw new Error(`No active source package is available for ${dependency}.`);
|
|
1126
1123
|
}
|
|
1127
1124
|
|
|
@@ -1175,6 +1172,10 @@ async function main() {
|
|
|
1175
1172
|
const headless = flags.yes || !process.stdin.isTTY;
|
|
1176
1173
|
Ie("Astrale \u2014 new domain");
|
|
1177
1174
|
let slug = flags.positional[0];
|
|
1175
|
+
if (flags.positional.length > 1) {
|
|
1176
|
+
xe(`Unexpected positional argument "${flags.positional[1]}".`);
|
|
1177
|
+
return 1;
|
|
1178
|
+
}
|
|
1178
1179
|
if (!slug) {
|
|
1179
1180
|
if (headless) {
|
|
1180
1181
|
xe("A domain slug is required in non-interactive mode (pass it as the first argument).");
|
|
@@ -1249,6 +1250,10 @@ async function main() {
|
|
|
1249
1250
|
pm = answer;
|
|
1250
1251
|
}
|
|
1251
1252
|
}
|
|
1253
|
+
if (!PMS.includes(pm)) {
|
|
1254
|
+
xe(`Unknown package manager "${pm}" \u2014 use ${PMS.join(", ")}.`);
|
|
1255
|
+
return 1;
|
|
1256
|
+
}
|
|
1252
1257
|
const dir = resolve2(flags.dir ?? slug);
|
|
1253
1258
|
if (existsSync2(dir) && !flags.force) {
|
|
1254
1259
|
if (headless) {
|
|
@@ -1259,8 +1264,10 @@ async function main() {
|
|
|
1259
1264
|
if (pD(go) || !go) return bail();
|
|
1260
1265
|
}
|
|
1261
1266
|
const link = flags.link ?? workspaceAvailable();
|
|
1262
|
-
if (flags.origin !== void 0 && !
|
|
1263
|
-
xe(
|
|
1267
|
+
if (flags.origin !== void 0 && !isValidOrigin(flags.origin)) {
|
|
1268
|
+
xe(
|
|
1269
|
+
`Invalid --origin "${flags.origin}". Use a lowercase dotted domain with valid DNS labels.`
|
|
1270
|
+
);
|
|
1264
1271
|
return 1;
|
|
1265
1272
|
}
|
|
1266
1273
|
const origin = flags.origin ?? (slug.includes(".") ? slug : `${slug}.example.dev`);
|
|
@@ -1341,16 +1348,24 @@ function parseFlags(argv) {
|
|
|
1341
1348
|
else if (a === "--force") flags.force = true;
|
|
1342
1349
|
else if (a === "--link") flags.link = true;
|
|
1343
1350
|
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
|
|
1351
|
+
else if (a === "--adapter") flags.adapter = flagValue(argv, ++i, a);
|
|
1352
|
+
else if (a === "--frontend") flags.frontend = flagValue(argv, ++i, a);
|
|
1353
|
+
else if (a === "--pm") flags.pm = flagValue(argv, ++i, a);
|
|
1354
|
+
else if (a === "--dir") flags.dir = flagValue(argv, ++i, a);
|
|
1355
|
+
else if (a === "--origin") flags.origin = flagValue(argv, ++i, a);
|
|
1356
|
+
else if (a === "--instance") flags.instance = flagValue(argv, ++i, a);
|
|
1357
|
+
else if (a.startsWith("-")) throw new TypeError(`Unknown option "${a}".`);
|
|
1350
1358
|
else if (!a.startsWith("-")) flags.positional.push(a);
|
|
1351
1359
|
}
|
|
1352
1360
|
return flags;
|
|
1353
1361
|
}
|
|
1362
|
+
function flagValue(argv, index, flag) {
|
|
1363
|
+
const value = argv[index];
|
|
1364
|
+
if (value === void 0 || value.startsWith("-")) {
|
|
1365
|
+
throw new TypeError(`${flag} requires a value.`);
|
|
1366
|
+
}
|
|
1367
|
+
return value;
|
|
1368
|
+
}
|
|
1354
1369
|
function printHelp() {
|
|
1355
1370
|
process.stdout.write(
|
|
1356
1371
|
`
|
|
@@ -1384,6 +1399,12 @@ Non-interactive (also auto-enabled without a TTY, e.g. CI):
|
|
|
1384
1399
|
function isValidSlug(v2) {
|
|
1385
1400
|
return /^[a-z][a-z0-9.-]*$/.test(v2);
|
|
1386
1401
|
}
|
|
1402
|
+
function isValidOrigin(value) {
|
|
1403
|
+
if (value.length > 253 || !value.includes(".")) return false;
|
|
1404
|
+
return value.split(".").every(
|
|
1405
|
+
(label) => label.length > 0 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label)
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1387
1408
|
function bail() {
|
|
1388
1409
|
xe("Cancelled.");
|
|
1389
1410
|
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.20",
|
|
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-
|
|
26
|
-
"@astrale-os/adapter-
|
|
31
|
+
"@astrale-os/adapter-astrale": "^0.5.0-beta.20",
|
|
32
|
+
"@astrale-os/adapter-cloudflare": "^0.5.0-beta.27"
|
|
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.6"
|
|
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.24"
|
|
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 })
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
# Astrale Domain
|
|
2
2
|
|
|
3
|
-
This project
|
|
4
|
-
`
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
This project is an ordinary Vite React application. `views/routes.ts` is the small Worker-safe
|
|
4
|
+
delivery artifact; `frontend/` owns TanStack Router and browser composition; `ui/` owns reusable
|
|
5
|
+
presentation. It has no mandatory Shell runtime: add `@astrale-os/shell-react` independently when
|
|
6
|
+
the application genuinely needs Shell sessions, graph context, or View-stage integration.
|
|
7
7
|
|
|
8
8
|
```text
|
|
9
9
|
schema/ public Domain contract, including View declarations
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
views/
|
|
13
|
-
|
|
10
|
+
runtime.ts Actions, Workflows, Integrations, Providers, and initialization
|
|
11
|
+
application.ts Schema, Runtime, and frontend composition
|
|
12
|
+
views/ schema-bound frontend declaration
|
|
13
|
+
frontend/ Vite, file-based TanStack Router, and Tailwind entrypoint
|
|
14
14
|
ui/ styles and pure presentation components
|
|
15
15
|
tests/ cross-layer executable journeys
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Run `pnpm dev` for
|
|
18
|
+
Run `pnpm dev` for one Worker-facing URL with Vite HMR, `pnpm build` to qualify Worker and frontend output,
|
|
19
19
|
and `pnpm prod` to deploy through the selected adapter. Add semantic `rules/`, `queries/`,
|
|
20
|
-
`mutations/`, and `
|
|
20
|
+
`mutations/`, and `actions/` modules only when the Domain gains those responsibilities.
|
|
21
21
|
|
|
22
22
|
Domain projects intentionally own no `.spec` trees. Import authoring contracts through semantic
|
|
23
|
-
`@astrale-os/sdk/*` subpaths; never import Kernel Core or DSL directly.
|
|
23
|
+
`@astrale-os/sdk/*` subpaths; never import Kernel Core or DSL directly. Routing remains a normal
|
|
24
|
+
TanStack Router concern; optional Astrale hooks remain normal React hooks inside route components.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { application } from '../application.js'
|
|
2
|
+
|
|
3
|
+
describe('react frontend variant', () => {
|
|
4
|
+
/** @evidence SDK-V1-SCAFFOLD-REACT */
|
|
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 })
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Astrale Domain</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|