@apex-stack/core 0.1.20 → 0.2.1
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/dist/{build-PETU3URU.js → build-VHS6KZBK.js} +75 -21
- package/dist/chunk-2C2HRLIY.js +18 -0
- package/dist/chunk-CHBSGOB3.js +42 -0
- package/dist/{chunk-G77MLFUJ.js → chunk-HCNNKT4A.js} +48 -9
- package/dist/chunk-JLIAISWM.js +48 -0
- package/dist/{chunk-4FUWZLVW.js → chunk-XDKJO6ZC.js} +148 -22
- package/dist/cli.js +16 -5
- package/dist/client.d.ts +1 -1
- package/dist/client.js +2 -1
- package/dist/{dev-6YCKNYJ4.js → dev-G7HPP6KW.js} +1 -1
- package/dist/index.d.ts +88 -5
- package/dist/index.js +12 -4
- package/dist/{make-WM6DLDCR.js → make-VAYO5GWA.js} +27 -3
- package/dist/{server-62UM2N5C.js → server-PTHGOE42.js} +41 -7
- package/dist/{start-V2TBGKWH.js → start-3O3E43PT.js} +44 -8
- package/dist/upgrade-WC5F5FKY.js +168 -0
- package/package.json +5 -4
- package/templates/default/.env.example +9 -0
- package/templates/default/README.md +25 -1
- package/templates/default/_gitignore +5 -0
- package/templates/default/apex.config.ts +22 -0
- package/vscode/apex-alpine.vsix +0 -0
- package/dist/chunk-HRJTOSYH.js +0 -8
- package/dist/chunk-MZVLRU3R.js +0 -15
|
@@ -2,15 +2,17 @@ import {
|
|
|
2
2
|
createApiHandler,
|
|
3
3
|
createMcpHandler,
|
|
4
4
|
expandApiModule,
|
|
5
|
-
hasMcpRoutes
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
hasMcpRoutes,
|
|
6
|
+
runMiddleware
|
|
7
|
+
} from "./chunk-HCNNKT4A.js";
|
|
8
|
+
import "./chunk-2C2HRLIY.js";
|
|
8
9
|
import {
|
|
10
|
+
applyEnvToRuntimeConfig,
|
|
9
11
|
matchRoute,
|
|
10
12
|
renderIslandsPage,
|
|
11
13
|
renderPage
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-XDKJO6ZC.js";
|
|
15
|
+
import "./chunk-JLIAISWM.js";
|
|
14
16
|
|
|
15
17
|
// src/commands/start.ts
|
|
16
18
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -25,6 +27,7 @@ import { pathToFileURL } from "url";
|
|
|
25
27
|
import {
|
|
26
28
|
createApp,
|
|
27
29
|
defineEventHandler,
|
|
30
|
+
getRequestHeaders,
|
|
28
31
|
getRequestURL,
|
|
29
32
|
setResponseHeader,
|
|
30
33
|
setResponseStatus,
|
|
@@ -46,6 +49,11 @@ async function startProdServer(options) {
|
|
|
46
49
|
const dir = options.dir;
|
|
47
50
|
const port = options.port ?? 3e3;
|
|
48
51
|
const manifest = JSON.parse(readFileSync(join(dir, "apex-manifest.json"), "utf8"));
|
|
52
|
+
const runtimeConfig = applyEnvToRuntimeConfig(
|
|
53
|
+
manifest.runtimeConfig ?? { public: {} },
|
|
54
|
+
process.cwd()
|
|
55
|
+
);
|
|
56
|
+
const publicConfig = runtimeConfig.public ?? {};
|
|
49
57
|
const importServer = (relFile) => import(pathToFileURL(join(dir, "server", relFile)).href);
|
|
50
58
|
const registry = {};
|
|
51
59
|
let componentCss = "";
|
|
@@ -60,7 +68,13 @@ async function startProdServer(options) {
|
|
|
60
68
|
const mod = await importServer(serverFile);
|
|
61
69
|
apiEntries.push(...expandApiModule(name, mod.default));
|
|
62
70
|
}
|
|
71
|
+
const middleware = [];
|
|
72
|
+
for (const { serverFile } of manifest.middleware ?? []) {
|
|
73
|
+
const mod = await importServer(serverFile);
|
|
74
|
+
if (typeof mod.default === "function") middleware.push(mod.default);
|
|
75
|
+
}
|
|
63
76
|
const serverFileFor = new Map(manifest.routes.map((r) => [r.pageId, r.serverFile]));
|
|
77
|
+
const errorPageId = serverFileFor.has("/pages/error.alpine") ? "/pages/error.alpine" : void 0;
|
|
64
78
|
const loadModule = (id) => importServer(serverFileFor.get(id));
|
|
65
79
|
const app = createApp();
|
|
66
80
|
app.use(
|
|
@@ -77,8 +91,26 @@ async function startProdServer(options) {
|
|
|
77
91
|
return readFileSync(file);
|
|
78
92
|
})
|
|
79
93
|
);
|
|
80
|
-
if (
|
|
81
|
-
|
|
94
|
+
if (middleware.length) {
|
|
95
|
+
app.use(
|
|
96
|
+
defineEventHandler(async (event) => {
|
|
97
|
+
const { redirect, locals } = await runMiddleware(middleware, {
|
|
98
|
+
url: getRequestURL(event).pathname,
|
|
99
|
+
method: event.method,
|
|
100
|
+
config: runtimeConfig,
|
|
101
|
+
headers: getRequestHeaders(event)
|
|
102
|
+
});
|
|
103
|
+
event.context.apexLocals = locals;
|
|
104
|
+
if (redirect) {
|
|
105
|
+
setResponseStatus(event, redirect.status);
|
|
106
|
+
setResponseHeader(event, "Location", redirect.to);
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if (apiEntries.length) app.use("/api", createApiHandler(apiEntries, runtimeConfig));
|
|
113
|
+
if (hasMcpRoutes(apiEntries)) app.use("/mcp", createMcpHandler(apiEntries, runtimeConfig));
|
|
82
114
|
app.use(
|
|
83
115
|
defineEventHandler(async (event) => {
|
|
84
116
|
const url = getRequestURL(event).pathname;
|
|
@@ -97,7 +129,11 @@ async function startProdServer(options) {
|
|
|
97
129
|
url,
|
|
98
130
|
registry,
|
|
99
131
|
componentCss,
|
|
100
|
-
clientHref: route?.clientHref
|
|
132
|
+
clientHref: route?.clientHref,
|
|
133
|
+
runtimeConfig,
|
|
134
|
+
publicConfig,
|
|
135
|
+
locals: event.context.apexLocals ?? {},
|
|
136
|
+
errorPageId
|
|
101
137
|
});
|
|
102
138
|
setResponseHeader(event, "Content-Type", "text/html");
|
|
103
139
|
return html;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
offerExtension
|
|
3
|
+
} from "./chunk-CHBSGOB3.js";
|
|
4
|
+
import {
|
|
5
|
+
VERSION,
|
|
6
|
+
banner,
|
|
7
|
+
color
|
|
8
|
+
} from "./chunk-QIXJSQLW.js";
|
|
9
|
+
|
|
10
|
+
// src/commands/upgrade.ts
|
|
11
|
+
import { spawnSync } from "child_process";
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
13
|
+
import { basename, dirname, join, relative, resolve } from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
import { defineCommand } from "citty";
|
|
16
|
+
var TEMPLATE_DIR = fileURLToPath(new URL("../templates/default", import.meta.url));
|
|
17
|
+
var PROTECTED = /* @__PURE__ */ new Set(["package.json"]);
|
|
18
|
+
function projectName(root) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(join(root, "package.json"), "utf8")).name || basename(root);
|
|
21
|
+
} catch {
|
|
22
|
+
return basename(root);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function bumpApexDeps(root) {
|
|
26
|
+
const pkgPath = join(root, "package.json");
|
|
27
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
28
|
+
let bumped = 0;
|
|
29
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
30
|
+
const deps = pkg[field];
|
|
31
|
+
if (!deps) continue;
|
|
32
|
+
for (const dep of Object.keys(deps)) {
|
|
33
|
+
if (dep.startsWith("@apex-stack/") && /^[\d^~]/.test(deps[dep] ?? "")) {
|
|
34
|
+
if (deps[dep] !== `^${VERSION}`) {
|
|
35
|
+
deps[dep] = `^${VERSION}`;
|
|
36
|
+
bumped++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (bumped) writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
42
|
+
`);
|
|
43
|
+
return bumped;
|
|
44
|
+
}
|
|
45
|
+
function detectPm() {
|
|
46
|
+
const ua = process.env.npm_config_user_agent || "";
|
|
47
|
+
if (ua.startsWith("pnpm")) return "pnpm";
|
|
48
|
+
if (ua.startsWith("yarn")) return "yarn";
|
|
49
|
+
if (ua.startsWith("bun")) return "bun";
|
|
50
|
+
return "npm";
|
|
51
|
+
}
|
|
52
|
+
function walk(dir, onFile) {
|
|
53
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
54
|
+
const p = join(dir, entry.name);
|
|
55
|
+
if (entry.isDirectory()) walk(p, onFile);
|
|
56
|
+
else onFile(p);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function targetRelPath(rel) {
|
|
60
|
+
return basename(rel) === "_gitignore" ? join(dirname(rel), ".gitignore") : rel;
|
|
61
|
+
}
|
|
62
|
+
var upgradeCommand = defineCommand({
|
|
63
|
+
meta: {
|
|
64
|
+
name: "upgrade",
|
|
65
|
+
description: "Adopt new scaffold defaults in an existing app (non-destructive)"
|
|
66
|
+
},
|
|
67
|
+
args: {
|
|
68
|
+
root: { type: "positional", required: false, description: "Project root", default: "." },
|
|
69
|
+
force: {
|
|
70
|
+
type: "boolean",
|
|
71
|
+
default: false,
|
|
72
|
+
description: "Re-sync files that differ from the template (package.json is always preserved)"
|
|
73
|
+
},
|
|
74
|
+
install: {
|
|
75
|
+
type: "boolean",
|
|
76
|
+
default: true,
|
|
77
|
+
description: "Run the package manager after bumping @apex-stack/* (use --no-install to skip)"
|
|
78
|
+
},
|
|
79
|
+
vscode: {
|
|
80
|
+
type: "boolean",
|
|
81
|
+
description: "Install the Apex VS Code extension (skip the prompt)"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
async run({ args }) {
|
|
85
|
+
const root = resolve(process.cwd(), String(args.root));
|
|
86
|
+
const log = console.log;
|
|
87
|
+
if (!existsSync(join(root, "package.json"))) {
|
|
88
|
+
console.error(`
|
|
89
|
+
${color.red("\u2717")} No package.json in ${root} \u2014 is this an Apex project?
|
|
90
|
+
`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
const name = projectName(root);
|
|
94
|
+
process.stdout.write(banner());
|
|
95
|
+
const added = [];
|
|
96
|
+
const updated = [];
|
|
97
|
+
let unchanged = 0;
|
|
98
|
+
walk(TEMPLATE_DIR, (absFile) => {
|
|
99
|
+
const rel = targetRelPath(relative(TEMPLATE_DIR, absFile));
|
|
100
|
+
const target = join(root, rel);
|
|
101
|
+
let content = readFileSync(absFile, "utf8");
|
|
102
|
+
if (content.includes("{{name}}")) content = content.replaceAll("{{name}}", name);
|
|
103
|
+
if (!existsSync(target)) {
|
|
104
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
105
|
+
writeFileSync(target, content);
|
|
106
|
+
added.push(rel);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (args.force && !PROTECTED.has(rel)) {
|
|
110
|
+
if (readFileSync(target, "utf8") !== content) {
|
|
111
|
+
writeFileSync(target, content);
|
|
112
|
+
updated.push(rel);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
unchanged++;
|
|
117
|
+
});
|
|
118
|
+
if (added.length) {
|
|
119
|
+
log(`
|
|
120
|
+
${color.green("+")} Added ${added.length} new file(s):`);
|
|
121
|
+
for (const f of added.sort()) log(` ${color.green(f)}`);
|
|
122
|
+
}
|
|
123
|
+
if (updated.length) {
|
|
124
|
+
log(`
|
|
125
|
+
${color.cyan("~")} Re-synced ${updated.length} file(s):`);
|
|
126
|
+
for (const f of updated.sort()) log(` ${color.cyan(f)}`);
|
|
127
|
+
}
|
|
128
|
+
if (added.length || updated.length) {
|
|
129
|
+
log(`
|
|
130
|
+
${color.gray(`${unchanged} existing file(s) left untouched.`)}`);
|
|
131
|
+
}
|
|
132
|
+
const bumped = bumpApexDeps(root);
|
|
133
|
+
if (bumped)
|
|
134
|
+
log(`
|
|
135
|
+
${color.cyan("\u2191")} Bumped ${bumped} @apex-stack/* dependency \u2192 ^${VERSION}`);
|
|
136
|
+
if (!added.length && !updated.length && !bumped) {
|
|
137
|
+
log(`
|
|
138
|
+
${color.green("\u2713")} Already up to date.`);
|
|
139
|
+
}
|
|
140
|
+
if (bumped && args.install) {
|
|
141
|
+
const pm = detectPm();
|
|
142
|
+
log(`
|
|
143
|
+
${color.gray(`Installing with ${pm}\u2026`)}`);
|
|
144
|
+
const ok = spawnSync(pm, ["install"], {
|
|
145
|
+
cwd: root,
|
|
146
|
+
stdio: "inherit",
|
|
147
|
+
shell: process.platform === "win32"
|
|
148
|
+
}).status === 0;
|
|
149
|
+
log(
|
|
150
|
+
ok ? ` ${color.green("\u2713")} Dependencies updated` : ` ${color.red("\u2717")} Install failed \u2014 run ${color.cyan(`${pm} install`)} yourself`
|
|
151
|
+
);
|
|
152
|
+
} else if (bumped) {
|
|
153
|
+
log(
|
|
154
|
+
` ${color.gray("Run")} ${color.cyan(`${detectPm()} install`)} ${color.gray("to apply.")}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const ext = await offerExtension(args.vscode);
|
|
158
|
+
if (ext) log(` ${color.green("\u2713")} ${ext}`);
|
|
159
|
+
log(
|
|
160
|
+
`
|
|
161
|
+
${color.gray("Non-destructive: your files are never overwritten")}${args.force ? color.gray(" except with --force") : color.gray(" (use --force to re-sync)")}${color.gray("; package.json entries are only version-bumped.")}
|
|
162
|
+
`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
export {
|
|
167
|
+
upgradeCommand
|
|
168
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apex-stack/core",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "The full-stack meta-framework for Alpine.js — CLI and runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
-
"templates"
|
|
40
|
+
"templates",
|
|
41
|
+
"vscode"
|
|
41
42
|
],
|
|
42
43
|
"dependencies": {
|
|
43
44
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -45,8 +46,8 @@
|
|
|
45
46
|
"h3": "^1.13.0",
|
|
46
47
|
"vite": "^6.0.7",
|
|
47
48
|
"zod": "^4.4.3",
|
|
48
|
-
"@apex-stack/kit": "0.
|
|
49
|
-
"@apex-stack/vite": "0.1.
|
|
49
|
+
"@apex-stack/kit": "0.2.0",
|
|
50
|
+
"@apex-stack/vite": "0.1.6"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"alpinejs": "^3.14.0"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Copy to `.env` and fill in. These override the defaults in apex.config.ts.
|
|
2
|
+
# Private keys: APEX_<KEY>. Public keys (sent to the browser): APEX_PUBLIC_<KEY>.
|
|
3
|
+
|
|
4
|
+
# Private — server-only
|
|
5
|
+
APEX_API_SECRET=
|
|
6
|
+
|
|
7
|
+
# Public — readable in the browser
|
|
8
|
+
APEX_PUBLIC_APP_NAME={{name}}
|
|
9
|
+
APEX_PUBLIC_SITE_URL=http://localhost:3000
|
|
@@ -49,7 +49,31 @@ apex make api todos
|
|
|
49
49
|
apex make service Billing # → services/BillingService.ts (OO class)
|
|
50
50
|
apex make store cart
|
|
51
51
|
apex make layout marketing
|
|
52
|
-
apex make
|
|
52
|
+
apex make middleware auth # → middleware/auth.ts (runs on every request)
|
|
53
|
+
apex make test billing # → tests/billing.test.ts
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Styling
|
|
57
|
+
|
|
58
|
+
- **Scoped by default.** A `<style scoped>` block in an `.alpine` file is scoped to that component.
|
|
59
|
+
- **Global / shared styles.** Create `app.css` (also `styles/app.css` or `src/app.css`) in the
|
|
60
|
+
project root — Apex auto-loads and processes it (Vite HMR in dev, bundled in build).
|
|
61
|
+
- **Tailwind.** `npm i tailwindcss @tailwindcss/vite`, then `@import "tailwindcss";` at the top of
|
|
62
|
+
`app.css`. Apex auto-detects `@tailwindcss/vite` — no extra config.
|
|
63
|
+
|
|
64
|
+
## Config & environment
|
|
65
|
+
|
|
66
|
+
`apex.config.ts` declares `runtimeConfig` (see the file's comments). `public.*` values reach the
|
|
67
|
+
browser; everything else is server-only. Override any value from `.env` — `APEX_<KEY>` (private) /
|
|
68
|
+
`APEX_PUBLIC_<KEY>` (public). Read it via the loader/route `config` argument, `useRuntimeConfig()`,
|
|
69
|
+
or `env('KEY', fallback)`. Real environment variables always win — build once, deploy with env.
|
|
70
|
+
|
|
71
|
+
## Upgrading
|
|
72
|
+
|
|
73
|
+
Adopt new scaffold defaults in this app without touching your code:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
apex upgrade # adds any new template files; never overwrites yours (package.json preserved)
|
|
53
77
|
```
|
|
54
78
|
|
|
55
79
|
Full docs: https://apexjs.site
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig } from '@apex-stack/core'
|
|
2
|
+
|
|
3
|
+
// Runtime configuration — declare defaults here, override any value from the
|
|
4
|
+
// environment. Private keys map to APEX_<KEY>; public keys to APEX_PUBLIC_<KEY>
|
|
5
|
+
// (camelCase ↔ SCREAMING_SNAKE). Values in .env win over these defaults.
|
|
6
|
+
//
|
|
7
|
+
// Read it anywhere:
|
|
8
|
+
// • loaders/routes: ({ config }) => config.public.appName
|
|
9
|
+
// • components/composables (client): useRuntimeConfig().public.appName
|
|
10
|
+
// • raw escape hatch: env('SOME_KEY', 'fallback')
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
runtimeConfig: {
|
|
13
|
+
// Private — server-only, never sent to the browser.
|
|
14
|
+
apiSecret: '', // ← APEX_API_SECRET
|
|
15
|
+
|
|
16
|
+
// Public — serialized into the page, readable in the browser.
|
|
17
|
+
public: {
|
|
18
|
+
appName: '{{name}}', // ← APEX_PUBLIC_APP_NAME
|
|
19
|
+
siteUrl: 'http://localhost:3000', // ← APEX_PUBLIC_SITE_URL
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
})
|
|
Binary file
|
package/dist/chunk-HRJTOSYH.js
DELETED
package/dist/chunk-MZVLRU3R.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// src/store.ts
|
|
2
|
-
function defineStore(name, factory) {
|
|
3
|
-
if (!name || /[^a-zA-Z0-9_$]/.test(name)) {
|
|
4
|
-
throw new Error(`defineStore: invalid store name "${name}" \u2014 use letters, digits, _ or $.`);
|
|
5
|
-
}
|
|
6
|
-
return { __apexStore: true, name, factory };
|
|
7
|
-
}
|
|
8
|
-
function isApexStore(x) {
|
|
9
|
-
return typeof x === "object" && x !== null && x.__apexStore === true;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
defineStore,
|
|
14
|
-
isApexStore
|
|
15
|
-
};
|