create-astrale-domain 0.2.13 → 0.3.0-beta.10
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 -0
- package/dist/index.js +197 -75
- package/package.json +10 -5
- package/template/.env.example +9 -0
- package/template/.gitignore.template +17 -0
- package/template/.variants/custom/README.md +20 -0
- package/template/.variants/custom/implementation.ts +14 -0
- package/template/.variants/custom/schema/application/index.ts +10 -0
- package/template/.variants/custom/ui/application/index.ts +2 -0
- package/template/.variants/custom/ui/application/styles.css +27 -0
- package/template/.variants/custom/ui/application/styles.d.ts +1 -0
- package/template/.variants/custom/ui/index.ts +1 -0
- package/template/.variants/custom/views/app/index.html +12 -0
- package/template/.variants/custom/views/app/main.ts +10 -0
- package/template/.variants/custom/views/application/__tests__/routes.test.ts +7 -0
- package/template/.variants/custom/views/application/routes.ts +12 -0
- package/template/.variants/custom/views/index.ts +1 -0
- package/template/.variants/custom/vite.config.ts +9 -0
- package/template/.variants/none/README.md +13 -0
- package/template/.variants/none/implementation.ts +14 -0
- package/template/.variants/none/schema/application/index.ts +7 -0
- package/template/.variants/react/README.md +23 -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 +18 -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 +19 -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/implementation.ts +16 -0
- package/template/.variants/react/schema/application/index.ts +10 -0
- package/template/.variants/react/ui/application/__tests__/application.test.tsx +13 -0
- package/template/.variants/react/ui/application/index.ts +1 -0
- package/template/.variants/react/ui/application/screen.tsx +18 -0
- package/template/.variants/react/ui/index.ts +1 -0
- package/template/.variants/react/views/application/__tests__/routes.test.ts +7 -0
- package/template/.variants/react/views/application/routes.ts +9 -0
- package/template/.variants/react/views/index.ts +1 -0
- package/template/.variants/react/vite.config.ts +35 -0
- package/template/CLAUDE.md +21 -0
- package/template/index.ts +2 -0
- package/template/oxlint.config.ts +1 -0
- package/template/package.json +77 -0
- package/template/pnpm-workspace.yaml +43 -0
- package/template/schema/application/__tests__/schema.test.ts +11 -0
- package/template/schema/index.ts +1 -0
- package/template/tests/e2e/application.test.ts +9 -0
- package/template/tests/index.ts +2 -0
- package/template/tsconfig.json +72 -0
- package/template/vitest.config.ts +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# `create-astrale-domain`
|
|
2
|
+
|
|
3
|
+
Official scaffolder for a new Astrale Domain project.
|
|
4
|
+
|
|
5
|
+
During the SDK beta program, generate a project from the beta cohort explicitly:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx -y create-astrale-domain@beta my-domain --yes
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The generated project must resolve the SDK and deployment adapters from npm; it must not retain
|
|
12
|
+
workspace links or repository-only source paths.
|
package/dist/index.js
CHANGED
|
@@ -779,28 +779,25 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
779
779
|
import { existsSync as existsSync2 } from "node:fs";
|
|
780
780
|
import { delimiter, join as join2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
781
781
|
|
|
782
|
-
// src/scaffold.ts
|
|
782
|
+
// src/scaffold/index.ts
|
|
783
783
|
import { existsSync, readFileSync } from "node:fs";
|
|
784
784
|
import { cp, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
785
785
|
import { createRequire } from "node:module";
|
|
786
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
786
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
787
787
|
import { fileURLToPath } from "node:url";
|
|
788
788
|
var require2 = createRequire(import.meta.url);
|
|
789
789
|
var HERE = dirname(fileURLToPath(import.meta.url));
|
|
790
|
-
var
|
|
790
|
+
var CREATE_PACKAGE_ROOT = owningPackageRoot(HERE, "create-astrale-domain");
|
|
791
|
+
var SDK_ROOT = sdkCheckoutRoot(CREATE_PACKAGE_ROOT);
|
|
791
792
|
var ORIGIN_PLACEHOLDER = "astrale-domain.example.dev";
|
|
792
793
|
var INSTANCE_PLACEHOLDER = "my-instance-slug";
|
|
793
|
-
var
|
|
794
|
-
"@astrale-os/sdk"
|
|
795
|
-
"@astrale-os/adapter-cloudflare"
|
|
796
|
-
"@astrale-os/adapter-astrale"
|
|
797
|
-
"@astrale-os/
|
|
798
|
-
"@astrale-os/
|
|
799
|
-
|
|
800
|
-
"@astrale-os/shell": "shell/packages/shell",
|
|
801
|
-
"@astrale-os/shell-react": "shell/packages/shell-react"
|
|
802
|
-
};
|
|
803
|
-
var BODY_PACKAGE = "@astrale-os/adapter-cloudflare";
|
|
794
|
+
var LINK_PACKAGES = /* @__PURE__ */ new Set([
|
|
795
|
+
"@astrale-os/sdk",
|
|
796
|
+
"@astrale-os/adapter-cloudflare",
|
|
797
|
+
"@astrale-os/adapter-astrale",
|
|
798
|
+
"@astrale-os/shell",
|
|
799
|
+
"@astrale-os/shell-react"
|
|
800
|
+
]);
|
|
804
801
|
var ADAPTER_PACKAGES = {
|
|
805
802
|
astrale: "@astrale-os/adapter-astrale",
|
|
806
803
|
cloudflare: "@astrale-os/adapter-cloudflare"
|
|
@@ -816,46 +813,68 @@ function errorText(err) {
|
|
|
816
813
|
if (err instanceof Error) return err.message;
|
|
817
814
|
return err === void 0 ? "" : String(err);
|
|
818
815
|
}
|
|
819
|
-
function errorReport(
|
|
820
|
-
if (!(
|
|
821
|
-
const cause =
|
|
816
|
+
function errorReport(error) {
|
|
817
|
+
if (!(error instanceof Error)) return String(error);
|
|
818
|
+
const cause = error.cause;
|
|
822
819
|
if (cause !== void 0) {
|
|
823
820
|
const causeText = cause instanceof Error ? cause.message : String(cause);
|
|
824
|
-
if (causeText && !
|
|
825
|
-
return `${
|
|
821
|
+
if (causeText && !error.message.includes(causeText)) {
|
|
822
|
+
return `${error.message}
|
|
826
823
|
\u21B3 caused by: ${causeText}`;
|
|
827
824
|
}
|
|
828
825
|
}
|
|
829
|
-
return
|
|
826
|
+
return error.message;
|
|
830
827
|
}
|
|
831
|
-
function packageRoot(
|
|
832
|
-
const
|
|
828
|
+
function packageRoot(packageName) {
|
|
829
|
+
const workspace = workspacePackageRoot(packageName);
|
|
830
|
+
if (workspace !== void 0) return workspace;
|
|
831
|
+
const direct = resolveModule(`${packageName}/package.json`);
|
|
833
832
|
if ("path" in direct) return dirname(direct.path);
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
833
|
+
const reason = errorText(direct.error);
|
|
834
|
+
throw new Error(
|
|
835
|
+
`Could not locate ${packageName}/package.json \u2014 install a release exposing its package metadata.` + (reason ? ` The module resolver failed: ${reason}` : ""),
|
|
836
|
+
direct.error === void 0 ? void 0 : { cause: direct.error }
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
function workspacePackageRoot(packageName) {
|
|
840
|
+
if (SDK_ROOT === void 0) return void 0;
|
|
841
|
+
const candidates = {
|
|
842
|
+
"@astrale-os/sdk": SDK_ROOT,
|
|
843
|
+
"@astrale-os/adapter-astrale": join(SDK_ROOT, "adapter-astrale"),
|
|
844
|
+
"@astrale-os/adapter-cloudflare": join(SDK_ROOT, "adapter-cloudflare"),
|
|
845
|
+
"create-astrale-domain": join(SDK_ROOT, "create-astrale-domain")
|
|
846
|
+
};
|
|
847
|
+
const candidate = candidates[packageName];
|
|
848
|
+
if (candidate === void 0 || !existsSync(join(candidate, "package.json"))) return void 0;
|
|
849
|
+
return candidate;
|
|
850
|
+
}
|
|
851
|
+
function owningPackageRoot(start, name) {
|
|
852
|
+
let directory = start;
|
|
853
|
+
for (; ; ) {
|
|
854
|
+
const manifest = join(directory, "package.json");
|
|
855
|
+
if (existsSync(manifest)) {
|
|
856
|
+
try {
|
|
857
|
+
const parsed = JSON.parse(readFileSync(manifest, "utf8"));
|
|
858
|
+
if (parsed.name === name) return directory;
|
|
859
|
+
} catch {
|
|
845
860
|
}
|
|
846
|
-
const parent = dirname(dir);
|
|
847
|
-
if (parent === dir) break;
|
|
848
|
-
dir = parent;
|
|
849
861
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
862
|
+
const parent = dirname(directory);
|
|
863
|
+
if (parent === directory) break;
|
|
864
|
+
directory = parent;
|
|
865
|
+
}
|
|
866
|
+
throw new Error(`Could not locate the ${name} package root from ${start}.`);
|
|
867
|
+
}
|
|
868
|
+
function sdkCheckoutRoot(createRoot) {
|
|
869
|
+
const candidate = dirname(createRoot);
|
|
870
|
+
const manifest = join(candidate, "package.json");
|
|
871
|
+
if (!existsSync(manifest)) return void 0;
|
|
872
|
+
try {
|
|
873
|
+
const parsed = JSON.parse(readFileSync(manifest, "utf8"));
|
|
874
|
+
return parsed.name === "@astrale-os/sdk" ? candidate : void 0;
|
|
875
|
+
} catch {
|
|
876
|
+
return void 0;
|
|
853
877
|
}
|
|
854
|
-
const reason = errorText(entry.error);
|
|
855
|
-
throw new Error(
|
|
856
|
-
`Could not locate the ${pkg} package \u2014 is it installed? (run your package manager's install)` + (reason ? ` The module resolver failed: ${reason}` : ""),
|
|
857
|
-
entry.error === void 0 ? void 0 : { cause: entry.error }
|
|
858
|
-
);
|
|
859
878
|
}
|
|
860
879
|
function installedPackageRange(pkg) {
|
|
861
880
|
const manifestPath = join(packageRoot(pkg), "package.json");
|
|
@@ -874,19 +893,22 @@ function installedPackageRange(pkg) {
|
|
|
874
893
|
function templateDir(pkg) {
|
|
875
894
|
return join(packageRoot(pkg), "template");
|
|
876
895
|
}
|
|
877
|
-
|
|
878
|
-
|
|
896
|
+
function bodyTemplateDir() {
|
|
897
|
+
return join(CREATE_PACKAGE_ROOT, "template");
|
|
898
|
+
}
|
|
899
|
+
async function scaffold(options) {
|
|
900
|
+
const preExisting = existsSync(options.dir);
|
|
879
901
|
try {
|
|
880
|
-
await writeProject(
|
|
902
|
+
await writeProject(options);
|
|
881
903
|
} catch (err) {
|
|
882
|
-
if (!preExisting && existsSync(
|
|
883
|
-
await rm(
|
|
904
|
+
if (!preExisting && existsSync(options.dir)) {
|
|
905
|
+
await rm(options.dir, { recursive: true, force: true });
|
|
884
906
|
}
|
|
885
907
|
throw err;
|
|
886
908
|
}
|
|
887
909
|
}
|
|
888
910
|
async function writeProject(opts) {
|
|
889
|
-
const src =
|
|
911
|
+
const src = bodyTemplateDir();
|
|
890
912
|
if (!existsSync(src)) {
|
|
891
913
|
throw new Error(`Template not found at ${src}.`);
|
|
892
914
|
}
|
|
@@ -896,15 +918,15 @@ async function writeProject(opts) {
|
|
|
896
918
|
}
|
|
897
919
|
await cp(src, opts.dir, {
|
|
898
920
|
recursive: true,
|
|
899
|
-
filter: (s) => !/\/(node_modules|\.agents|\.astrale|\.dist|\.domain-studio
|
|
921
|
+
filter: (s) => !/\/(node_modules|client|\.agents|\.astrale|\.dist|\.domain-studio|\.variants|\.wrangler|pnpm-lock\.yaml|\.DS_Store)(\/|$)/.test(
|
|
900
922
|
`/${relative(src, s)}`
|
|
901
923
|
)
|
|
902
924
|
});
|
|
903
|
-
await
|
|
925
|
+
await materializeFrontend(opts, src);
|
|
926
|
+
await materializeGitignores(opts.dir, opts.frontend);
|
|
904
927
|
await materializeAdapter(opts);
|
|
905
928
|
await stampOrigin(opts.dir, opts.origin);
|
|
906
929
|
await rewritePackageJson(opts.dir, opts.name, opts.link);
|
|
907
|
-
await rewriteWorkspaceMember(join(opts.dir, "client", "package.json"), opts.link);
|
|
908
930
|
if (opts.link) await writeLinkedZodOverride(opts.dir);
|
|
909
931
|
else await writeReleaseAgePolicy(opts.dir);
|
|
910
932
|
await writeFile(
|
|
@@ -912,8 +934,8 @@ async function writeProject(opts) {
|
|
|
912
934
|
"# Dev secrets \u2014 the ENTIRE file is injected into the local runtime by `pnpm dev`.\n# Gitignored. See .env.example.\n"
|
|
913
935
|
);
|
|
914
936
|
}
|
|
915
|
-
async function materializeGitignores(dir) {
|
|
916
|
-
for (const path of [join(dir, ".gitignore")
|
|
937
|
+
async function materializeGitignores(dir, frontend) {
|
|
938
|
+
for (const path of [join(dir, ".gitignore")]) {
|
|
917
939
|
const packagedPath = `${path}.template`;
|
|
918
940
|
if (existsSync(packagedPath)) {
|
|
919
941
|
await rename(packagedPath, path);
|
|
@@ -921,6 +943,49 @@ async function materializeGitignores(dir) {
|
|
|
921
943
|
throw new Error(`Template is missing packaged Git ignore file ${packagedPath}.`);
|
|
922
944
|
}
|
|
923
945
|
}
|
|
946
|
+
void frontend;
|
|
947
|
+
}
|
|
948
|
+
async function materializeFrontend(opts, template) {
|
|
949
|
+
const variant = join(template, ".variants", opts.frontend);
|
|
950
|
+
if (!existsSync(variant)) throw new Error(`Frontend template not found at ${variant}.`);
|
|
951
|
+
await cp(variant, opts.dir, { recursive: true });
|
|
952
|
+
const path = join(opts.dir, "package.json");
|
|
953
|
+
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
954
|
+
pkg.dependencies ??= {};
|
|
955
|
+
pkg.devDependencies ??= {};
|
|
956
|
+
pkg.scripts ??= {};
|
|
957
|
+
if (opts.frontend === "react") {
|
|
958
|
+
Object.assign(pkg.dependencies, {
|
|
959
|
+
"@astrale-os/shell": ">=0.3.8-beta.2 <1.0.0",
|
|
960
|
+
"@astrale-os/shell-react": ">=0.2.10-beta.2 <1.0.0",
|
|
961
|
+
"@tanstack/react-router": "~1.131.0",
|
|
962
|
+
react: "^19.2.0",
|
|
963
|
+
"react-dom": "^19.2.0"
|
|
964
|
+
});
|
|
965
|
+
Object.assign(pkg.devDependencies, {
|
|
966
|
+
"@tailwindcss/vite": "^4.0.6",
|
|
967
|
+
"@tanstack/router-plugin": "~1.131.0",
|
|
968
|
+
"@types/react": "^19.2.0",
|
|
969
|
+
"@types/react-dom": "^19.2.0",
|
|
970
|
+
"@vitejs/plugin-react": "^5.0.4",
|
|
971
|
+
tailwindcss: "^4.0.6",
|
|
972
|
+
vite: "^7.1.7"
|
|
973
|
+
});
|
|
974
|
+
} else if (opts.frontend === "custom") {
|
|
975
|
+
pkg.devDependencies.vite = "^7.1.7";
|
|
976
|
+
}
|
|
977
|
+
if (opts.frontend !== "none") {
|
|
978
|
+
pkg.scripts["frontend:dev"] = "vite";
|
|
979
|
+
pkg.scripts["frontend:build"] = "vite build";
|
|
980
|
+
}
|
|
981
|
+
pkg.dependencies = sorted(pkg.dependencies);
|
|
982
|
+
pkg.devDependencies = sorted(pkg.devDependencies);
|
|
983
|
+
await writeFile(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
984
|
+
}
|
|
985
|
+
function sorted(values) {
|
|
986
|
+
return Object.fromEntries(
|
|
987
|
+
Object.entries(values).sort(([left], [right]) => left.localeCompare(right))
|
|
988
|
+
);
|
|
924
989
|
}
|
|
925
990
|
function assertSafeToDelete(dir) {
|
|
926
991
|
const target = resolve(dir);
|
|
@@ -932,40 +997,34 @@ function assertSafeToDelete(dir) {
|
|
|
932
997
|
}
|
|
933
998
|
}
|
|
934
999
|
async function stampOrigin(dir, origin) {
|
|
935
|
-
const schemaIndex = join(dir, "schema", "index.ts");
|
|
1000
|
+
const schemaIndex = join(dir, "schema", "application", "index.ts");
|
|
936
1001
|
await replaceInFile(schemaIndex, ORIGIN_PLACEHOLDER, origin);
|
|
937
1002
|
const config = join(dir, "astrale.config.ts");
|
|
938
1003
|
await replaceInFile(config, ORIGIN_PLACEHOLDER, origin);
|
|
939
1004
|
}
|
|
940
1005
|
async function materializeAdapter(opts) {
|
|
941
|
-
if (opts.adapter !== "astrale") return;
|
|
942
1006
|
const config = join(opts.dir, "astrale.config.ts");
|
|
943
|
-
await cp(join(templateDir(ADAPTER_PACKAGES.
|
|
944
|
-
|
|
1007
|
+
await cp(join(templateDir(ADAPTER_PACKAGES[opts.adapter]), "astrale.config.ts"), config);
|
|
1008
|
+
if (opts.adapter === "cloudflare") return;
|
|
1009
|
+
await addManagedAdapterDep(opts.dir);
|
|
945
1010
|
if (opts.instance) await replaceInFile(config, `'${INSTANCE_PLACEHOLDER}'`, `'${opts.instance}'`);
|
|
946
1011
|
}
|
|
947
|
-
async function
|
|
1012
|
+
async function addManagedAdapterDep(dir) {
|
|
948
1013
|
const path = join(dir, "package.json");
|
|
949
1014
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
950
|
-
if (!pkg.dependencies || !(
|
|
1015
|
+
if (!pkg.dependencies || !("@astrale-os/adapter-cloudflare" in pkg.dependencies)) {
|
|
951
1016
|
throw new Error(
|
|
952
|
-
|
|
1017
|
+
"Could not materialize @astrale-os/adapter-astrale: the scaffold body does not declare its @astrale-os/adapter-cloudflare Worker runtime dependency."
|
|
953
1018
|
);
|
|
954
1019
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1020
|
+
pkg.dependencies["@astrale-os/adapter-astrale"] = installedPackageRange(
|
|
1021
|
+
"@astrale-os/adapter-astrale"
|
|
1022
|
+
);
|
|
958
1023
|
pkg.dependencies = Object.fromEntries(
|
|
959
1024
|
Object.entries(pkg.dependencies).sort(([a], [b3]) => a.localeCompare(b3))
|
|
960
1025
|
);
|
|
961
1026
|
await writeFile(path, JSON.stringify(pkg, null, 2) + "\n");
|
|
962
1027
|
}
|
|
963
|
-
async function rewriteWorkspaceMember(pkgPath, link) {
|
|
964
|
-
if (!existsSync(pkgPath)) return;
|
|
965
|
-
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
966
|
-
const changed = rewriteAstraleDeps(pkg, link);
|
|
967
|
-
if (changed) await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
968
|
-
}
|
|
969
1028
|
async function rewritePackageJson(dir, name, link) {
|
|
970
1029
|
const path = join(dir, "package.json");
|
|
971
1030
|
const pkg = JSON.parse(await readFile(path, "utf-8"));
|
|
@@ -979,8 +1038,8 @@ function rewriteAstraleDeps(pkg, link) {
|
|
|
979
1038
|
for (const group of [pkg.dependencies, pkg.devDependencies]) {
|
|
980
1039
|
if (!group) continue;
|
|
981
1040
|
for (const dep of Object.keys(group)) {
|
|
982
|
-
if (!
|
|
983
|
-
const next = `link:${
|
|
1041
|
+
if (!LINK_PACKAGES.has(dep)) continue;
|
|
1042
|
+
const next = `link:${localPackageRoot(dep)}`;
|
|
984
1043
|
if (group[dep] !== next) {
|
|
985
1044
|
group[dep] = next;
|
|
986
1045
|
changed = true;
|
|
@@ -1024,7 +1083,8 @@ function escapeRegExp(value) {
|
|
|
1024
1083
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1025
1084
|
}
|
|
1026
1085
|
function sdkZodVersion() {
|
|
1027
|
-
|
|
1086
|
+
if (SDK_ROOT === void 0) throw new Error("Local SDK checkout is unavailable.");
|
|
1087
|
+
const sdkRequire = createRequire(join(SDK_ROOT, "package.json"));
|
|
1028
1088
|
const pkg = sdkRequire("zod/package.json");
|
|
1029
1089
|
return pkg.version;
|
|
1030
1090
|
}
|
|
@@ -1035,7 +1095,32 @@ async function replaceInFile(path, from, to) {
|
|
|
1035
1095
|
if (next !== text) await writeFile(path, next);
|
|
1036
1096
|
}
|
|
1037
1097
|
function workspaceAvailable() {
|
|
1038
|
-
|
|
1098
|
+
if (SDK_ROOT === void 0) return false;
|
|
1099
|
+
try {
|
|
1100
|
+
for (const dependency of LINK_PACKAGES) localPackageRoot(dependency);
|
|
1101
|
+
return true;
|
|
1102
|
+
} catch {
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
function localPackageRoot(dependency) {
|
|
1107
|
+
if (SDK_ROOT !== void 0) {
|
|
1108
|
+
const local = dependency === "@astrale-os/sdk" ? SDK_ROOT : dependency === "@astrale-os/adapter-cloudflare" ? join(SDK_ROOT, "adapter-cloudflare") : dependency === "@astrale-os/adapter-astrale" ? join(SDK_ROOT, "adapter-astrale") : void 0;
|
|
1109
|
+
if (local !== void 0 && existsSync(join(local, "package.json"))) return local;
|
|
1110
|
+
}
|
|
1111
|
+
const resolved = resolveModule(`${dependency}/package.json`);
|
|
1112
|
+
if ("path" in resolved) {
|
|
1113
|
+
const installed = dirname(resolved.path);
|
|
1114
|
+
if (!installed.split(sep).includes("node_modules")) return installed;
|
|
1115
|
+
}
|
|
1116
|
+
if ((dependency === "@astrale-os/shell" || dependency === "@astrale-os/shell-react") && SDK_ROOT !== void 0) {
|
|
1117
|
+
const packageDirectory = dependency === "@astrale-os/shell" ? "shell" : "shell-react";
|
|
1118
|
+
for (const base of [dirname(SDK_ROOT), dirname(dirname(SDK_ROOT))]) {
|
|
1119
|
+
const candidate = join(base, "shell", "packages", packageDirectory);
|
|
1120
|
+
if (existsSync(join(candidate, "package.json"))) return candidate;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
throw new Error(`No active source package is available for ${dependency}.`);
|
|
1039
1124
|
}
|
|
1040
1125
|
|
|
1041
1126
|
// src/index.ts
|
|
@@ -1061,6 +1146,23 @@ var ADAPTERS = [
|
|
|
1061
1146
|
},
|
|
1062
1147
|
{ value: "custom", label: "Custom", hint: "bring your own adapter (soon)", enabled: false }
|
|
1063
1148
|
];
|
|
1149
|
+
var FRONTENDS = [
|
|
1150
|
+
{
|
|
1151
|
+
value: "react",
|
|
1152
|
+
label: "React",
|
|
1153
|
+
hint: "managed shell integration with typed View component coordinates"
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
value: "custom",
|
|
1157
|
+
label: "Custom",
|
|
1158
|
+
hint: "framework-neutral frontend artifact with a minimal Vite application"
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
value: "none",
|
|
1162
|
+
label: "None",
|
|
1163
|
+
hint: "callable Domain only; no Views or browser assets"
|
|
1164
|
+
}
|
|
1165
|
+
];
|
|
1064
1166
|
async function main() {
|
|
1065
1167
|
const argv = process.argv.slice(2);
|
|
1066
1168
|
const flags = parseFlags(argv);
|
|
@@ -1109,6 +1211,23 @@ async function main() {
|
|
|
1109
1211
|
xe(`The "${adapter}" adapter is not available yet \u2014 use "astrale" or "cloudflare".`);
|
|
1110
1212
|
return 1;
|
|
1111
1213
|
}
|
|
1214
|
+
let frontend = flags.frontend;
|
|
1215
|
+
if (!frontend) {
|
|
1216
|
+
if (headless) frontend = "react";
|
|
1217
|
+
else {
|
|
1218
|
+
const answer = await ve({
|
|
1219
|
+
message: "Frontend",
|
|
1220
|
+
initialValue: "react",
|
|
1221
|
+
options: [...FRONTENDS]
|
|
1222
|
+
});
|
|
1223
|
+
if (pD(answer)) return bail();
|
|
1224
|
+
frontend = answer;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (frontend !== "react" && frontend !== "custom" && frontend !== "none") {
|
|
1228
|
+
xe(`Unknown frontend "${frontend}" \u2014 use "react", "custom", or "none".`);
|
|
1229
|
+
return 1;
|
|
1230
|
+
}
|
|
1112
1231
|
const instance = flags.instance;
|
|
1113
1232
|
if (instance !== void 0 && !isValidSlug(instance)) {
|
|
1114
1233
|
xe(`Invalid --instance "${instance}". Use lowercase letters, digits, dots and dashes.`);
|
|
@@ -1151,6 +1270,7 @@ async function main() {
|
|
|
1151
1270
|
name: slug,
|
|
1152
1271
|
origin,
|
|
1153
1272
|
adapter,
|
|
1273
|
+
frontend,
|
|
1154
1274
|
instance,
|
|
1155
1275
|
link
|
|
1156
1276
|
});
|
|
@@ -1220,6 +1340,7 @@ function parseFlags(argv) {
|
|
|
1220
1340
|
else if (a === "--link") flags.link = true;
|
|
1221
1341
|
else if (a === "--no-link") flags.link = false;
|
|
1222
1342
|
else if (a === "--adapter") flags.adapter = argv[++i];
|
|
1343
|
+
else if (a === "--frontend") flags.frontend = argv[++i];
|
|
1223
1344
|
else if (a === "--pm") flags.pm = argv[++i];
|
|
1224
1345
|
else if (a === "--dir") flags.dir = argv[++i];
|
|
1225
1346
|
else if (a === "--origin") flags.origin = argv[++i];
|
|
@@ -1242,6 +1363,7 @@ Arguments:
|
|
|
1242
1363
|
|
|
1243
1364
|
Options:
|
|
1244
1365
|
--adapter <id> deployment adapter \u2014 astrale (managed, default) | cloudflare
|
|
1366
|
+
--frontend <id> react (default) | custom | none
|
|
1245
1367
|
--instance <slug> target instance for managed deploys (astrale adapter)
|
|
1246
1368
|
--pm <pm> pnpm | npm | yarn | bun (default: detected, else pnpm)
|
|
1247
1369
|
--dir <path> target directory (default: ./<slug>)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-astrale-domain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-beta.10",
|
|
4
4
|
"description": "Scaffold a standalone Astrale domain — npm create astrale-domain",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astrale",
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
"create-astrale-domain": "./dist/index.js"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
|
-
"dist"
|
|
17
|
+
"dist",
|
|
18
|
+
"template",
|
|
19
|
+
"!template/node_modules",
|
|
20
|
+
"!template/pnpm-lock.yaml"
|
|
18
21
|
],
|
|
19
22
|
"type": "module",
|
|
20
23
|
"dependencies": {
|
|
21
24
|
"@clack/prompts": "^0.11.0",
|
|
22
|
-
"@astrale-os/adapter-astrale": "^0.
|
|
23
|
-
"@astrale-os/adapter-cloudflare": "^0.
|
|
25
|
+
"@astrale-os/adapter-astrale": "^0.5.0-beta.10",
|
|
26
|
+
"@astrale-os/adapter-cloudflare": "^0.5.0-beta.17"
|
|
24
27
|
},
|
|
25
28
|
"devDependencies": {
|
|
26
29
|
"@astrale-os/ox": ">=0.1.0 <1.0.0",
|
|
@@ -30,7 +33,8 @@
|
|
|
30
33
|
"oxfmt": "0.52.0",
|
|
31
34
|
"oxlint": "latest",
|
|
32
35
|
"typescript": "~6.0.1-rc",
|
|
33
|
-
"vitest": "^3.0.0"
|
|
36
|
+
"vitest": "^3.0.0",
|
|
37
|
+
"@astrale-os/sdk": "^0.5.0-beta.14"
|
|
34
38
|
},
|
|
35
39
|
"engines": {
|
|
36
40
|
"node": ">=22"
|
|
@@ -39,6 +43,7 @@
|
|
|
39
43
|
"build": "node scripts/build.mjs",
|
|
40
44
|
"typecheck": "tsgo --noEmit",
|
|
41
45
|
"test": "vitest run",
|
|
46
|
+
"test:generated": "node scripts/verify-generated.mjs",
|
|
42
47
|
"lint": "oxlint .",
|
|
43
48
|
"format": "pnpm exec oxfmt --write ."
|
|
44
49
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Secrets for an env live in `.env.<env>` (e.g. .env.dev, .env.prod), pointed at
|
|
2
|
+
# by `secrets:` in astrale.config.ts. The ENTIRE file is treated as secrets:
|
|
3
|
+
# injected into the local runtime in dev, pushed to the Worker secret store in
|
|
4
|
+
# prod. These files are gitignored — copy this to `.env.dev` and fill in.
|
|
5
|
+
#
|
|
6
|
+
# Add one line per secret required by an Integration. Declare the corresponding
|
|
7
|
+
# binding on `ApplicationEnvironment` in `implementation.ts`. For example:
|
|
8
|
+
#
|
|
9
|
+
# EXAMPLE_API_KEY=sk-...
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Deps
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Generated plumbing (worker entry, wrangler config, identity, spec)
|
|
5
|
+
.astrale/
|
|
6
|
+
.domain-studio/.cache/
|
|
7
|
+
|
|
8
|
+
# Worker dev + build artifacts
|
|
9
|
+
.dev.vars
|
|
10
|
+
.wrangler/
|
|
11
|
+
.dist/
|
|
12
|
+
dist/
|
|
13
|
+
|
|
14
|
+
# Secrets — every .env.<env> is gitignored; .env.example is the only one tracked
|
|
15
|
+
.env
|
|
16
|
+
.env.*
|
|
17
|
+
!.env.example
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Astrale Domain
|
|
2
|
+
|
|
3
|
+
This project uses the framework-neutral frontend escape hatch. `views/routes.ts` binds declared
|
|
4
|
+
Views to a `frontendArtifact`; `vite.config.ts` owns the ordinary Vite root you can replace with any
|
|
5
|
+
framework; `ui/` owns presentation assets. The starter route uses `handshake: 'none'`, so it mounts
|
|
6
|
+
as a plain hosted app. Change that route to `handshake: 'shell'` only after your browser entrypoint
|
|
7
|
+
boots the Shell protocol.
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
schema/ public Domain contract, including View declarations
|
|
11
|
+
implementation.ts schema, callable handlers, and frontend composition
|
|
12
|
+
views/routes.ts framework-neutral artifact and routes
|
|
13
|
+
views/app/ fully author-owned browser application
|
|
14
|
+
ui/ presentation tokens and styles
|
|
15
|
+
tests/ cross-layer executable journeys
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run `pnpm dev` for local delivery and `pnpm build` to build both the Worker and browser assets.
|
|
19
|
+
Domain projects intentionally own no `.spec` trees and import authoring contracts only through
|
|
20
|
+
semantic `@astrale-os/sdk/*` subpaths.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineDomain, type DomainHandlers } from '@astrale-os/sdk'
|
|
2
|
+
|
|
3
|
+
import { schema } from '#schema'
|
|
4
|
+
import { frontend } from '#views'
|
|
5
|
+
|
|
6
|
+
export interface ApplicationEnvironment {}
|
|
7
|
+
|
|
8
|
+
const handlers = {
|
|
9
|
+
functions: {},
|
|
10
|
+
classes: {},
|
|
11
|
+
interfaces: {},
|
|
12
|
+
} satisfies DomainHandlers<typeof schema, ApplicationEnvironment>
|
|
13
|
+
|
|
14
|
+
export const domain = defineDomain({ schema, handlers, frontend })
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color: #f5f3ed;
|
|
3
|
+
background: #171714;
|
|
4
|
+
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
display: grid;
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
align-content: center;
|
|
12
|
+
max-width: 64rem;
|
|
13
|
+
padding: 4rem;
|
|
14
|
+
margin: 0 auto;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
h1 {
|
|
18
|
+
margin: 0;
|
|
19
|
+
font-size: clamp(3rem, 9vw, 7rem);
|
|
20
|
+
line-height: 0.92;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.application-eyebrow {
|
|
24
|
+
color: #e3b866;
|
|
25
|
+
font-family: ui-monospace, monospace;
|
|
26
|
+
text-transform: uppercase;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module '*.css' {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { applicationTheme } from './application/index.js'
|
|
@@ -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
|
+
<main id="application">Loading…</main>
|
|
10
|
+
<script type="module" src="/main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import '../../ui/application/styles.css'
|
|
2
|
+
|
|
3
|
+
const application = document.querySelector<HTMLElement>('#application')
|
|
4
|
+
if (application === null) throw new Error('Custom frontend root #application is missing.')
|
|
5
|
+
|
|
6
|
+
application.innerHTML = `
|
|
7
|
+
<p class="application-eyebrow">Custom frontend</p>
|
|
8
|
+
<h1>Ready to build.</h1>
|
|
9
|
+
<p>Replace views/app with any framework while preserving views/application/routes.ts.</p>
|
|
10
|
+
`
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { frontendArtifact, viteFrontend } from '@astrale-os/sdk/view'
|
|
2
|
+
|
|
3
|
+
import { schema } from '#schema'
|
|
4
|
+
|
|
5
|
+
/** Framework-neutral escape hatch: swap Vite or the browser implementation without changing Views. */
|
|
6
|
+
export const frontend = frontendArtifact({
|
|
7
|
+
schema,
|
|
8
|
+
source: viteFrontend(),
|
|
9
|
+
routes: {
|
|
10
|
+
application: { handshake: 'none' },
|
|
11
|
+
},
|
|
12
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { frontend } from './application/routes.js'
|