fastscript 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +17 -0
- package/CHANGELOG.md +5 -0
- package/Dockerfile +9 -0
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/app/api/auth.js +10 -0
- package/app/api/hello.js +3 -0
- package/app/api/upload.js +9 -0
- package/app/api/webhook.js +10 -0
- package/app/db/migrations/001_init.js +6 -0
- package/app/db/seed.js +5 -0
- package/app/env.schema.js +6 -0
- package/app/middleware.fs +7 -0
- package/app/pages/404.fs +3 -0
- package/app/pages/_layout.fs +17 -0
- package/app/pages/benchmarks.fs +15 -0
- package/app/pages/docs/index.fs +16 -0
- package/app/pages/index.fs +22 -0
- package/app/pages/private.fs +12 -0
- package/app/pages/showcase.fs +14 -0
- package/app/styles.css +14 -0
- package/docs/AI_CONTEXT_PACK_V1.md +25 -0
- package/docs/DEPLOY_GUIDE.md +14 -0
- package/docs/INCIDENT_PLAYBOOK.md +18 -0
- package/docs/INTEROP_RULES.md +7 -0
- package/docs/PLUGIN_API_CONTRACT.md +22 -0
- package/ecosystem.config.cjs +1 -0
- package/examples/fullstack/README.md +10 -0
- package/examples/fullstack/app/api/orders.js +8 -0
- package/examples/fullstack/app/db/migrations/001_init.js +3 -0
- package/examples/fullstack/app/db/seed.js +3 -0
- package/examples/fullstack/app/jobs/send-order-email.js +4 -0
- package/examples/fullstack/app/pages/_layout.fs +1 -0
- package/examples/fullstack/app/pages/index.fs +3 -0
- package/examples/startup-mvp/README.md +8 -0
- package/examples/startup-mvp/app/api/cart.js +9 -0
- package/examples/startup-mvp/app/api/checkout.js +8 -0
- package/examples/startup-mvp/app/db/migrations/001_products.js +6 -0
- package/examples/startup-mvp/app/jobs/send-receipt.js +4 -0
- package/examples/startup-mvp/app/pages/_layout.fs +3 -0
- package/examples/startup-mvp/app/pages/dashboard/index.fs +9 -0
- package/examples/startup-mvp/app/pages/index.fs +18 -0
- package/package.json +50 -0
- package/scripts/bench-report.mjs +36 -0
- package/scripts/release.mjs +21 -0
- package/scripts/smoke-dev.mjs +78 -0
- package/scripts/smoke-start.mjs +41 -0
- package/scripts/test-auth.mjs +26 -0
- package/scripts/test-db.mjs +31 -0
- package/scripts/test-jobs.mjs +15 -0
- package/scripts/test-middleware.mjs +37 -0
- package/scripts/test-roundtrip.mjs +44 -0
- package/scripts/test-validation.mjs +17 -0
- package/scripts/test-webhook-storage.mjs +22 -0
- package/spec/FASTSCRIPT_1000_BUILD_LIST.md +1090 -0
- package/src/auth-flows.mjs +29 -0
- package/src/auth.mjs +115 -0
- package/src/bench.mjs +46 -0
- package/src/build.mjs +222 -0
- package/src/cache.mjs +58 -0
- package/src/check.mjs +22 -0
- package/src/cli.mjs +71 -0
- package/src/compat.mjs +122 -0
- package/src/create.mjs +190 -0
- package/src/db-cli.mjs +45 -0
- package/src/db-postgres.mjs +40 -0
- package/src/db.mjs +103 -0
- package/src/deploy.mjs +65 -0
- package/src/dev.mjs +5 -0
- package/src/env.mjs +89 -0
- package/src/export.mjs +83 -0
- package/src/fs-normalize.mjs +100 -0
- package/src/interop.mjs +16 -0
- package/src/jobs.mjs +127 -0
- package/src/logger.mjs +27 -0
- package/src/middleware.mjs +14 -0
- package/src/migrate.mjs +81 -0
- package/src/observability.mjs +21 -0
- package/src/security.mjs +55 -0
- package/src/server-runtime.mjs +339 -0
- package/src/start.mjs +10 -0
- package/src/storage.mjs +56 -0
- package/src/validate.mjs +18 -0
- package/src/validation.mjs +79 -0
- package/src/webhook.mjs +71 -0
- package/src/worker.mjs +5 -0
- package/vercel.json +15 -0
- package/vscode/fastscript-language/README.md +12 -0
- package/vscode/fastscript-language/extension.js +24 -0
- package/vscode/fastscript-language/language-configuration.json +6 -0
- package/vscode/fastscript-language/lsp/server.cjs +27 -0
- package/vscode/fastscript-language/lsp/smoke-test.cjs +1 -0
- package/vscode/fastscript-language/package.json +36 -0
- package/vscode/fastscript-language/snippets/fastscript.code-snippets +24 -0
- package/vscode/fastscript-language/syntaxes/fastscript.tmLanguage.json +21 -0
- package/wrangler.toml +5 -0
package/CHANGELOG.md
ADDED
package/Dockerfile
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FastScript
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# FastScript
|
|
2
|
+
|
|
3
|
+
FastScript is a JavaScript-first full-stack framework focused on three things:
|
|
4
|
+
|
|
5
|
+
- Simpler than heavy framework stacks
|
|
6
|
+
- Faster build and runtime pipeline
|
|
7
|
+
- Compatible with existing JavaScript ecosystem
|
|
8
|
+
- `.fs` first, `.js` always supported
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install
|
|
14
|
+
npm run create
|
|
15
|
+
npm run dev
|
|
16
|
+
npm run start
|
|
17
|
+
npm run build
|
|
18
|
+
npm run check
|
|
19
|
+
npm run migrate
|
|
20
|
+
npm run bench
|
|
21
|
+
npm run export:js
|
|
22
|
+
npm run export:ts
|
|
23
|
+
npm run compat
|
|
24
|
+
npm run validate
|
|
25
|
+
npm run db:migrate
|
|
26
|
+
npm run db:seed
|
|
27
|
+
npm run smoke:dev
|
|
28
|
+
npm run smoke:start
|
|
29
|
+
npm run test:core
|
|
30
|
+
npm run bench:report
|
|
31
|
+
npm run qa:all
|
|
32
|
+
npm run worker
|
|
33
|
+
npm run deploy:node
|
|
34
|
+
npm run deploy:vercel
|
|
35
|
+
npm run deploy:cloudflare
|
|
36
|
+
npm run release:patch
|
|
37
|
+
npm run pack:check
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- `npm run migrate`: convert `app/pages` files (`.js/.jsx/.ts/.tsx`) to `.fs`
|
|
41
|
+
- `npm run bench`: enforce 3G-oriented gzip budgets on built output
|
|
42
|
+
- `npm run export:js`: export `.fs` app source to plain `.js` project
|
|
43
|
+
- `npm run export:ts`: export `.fs` app source to `.ts` project
|
|
44
|
+
- `npm run compat`: run ESM/CJS/FS interop smoke checks
|
|
45
|
+
- `npm run db:migrate`: run database migrations from `app/db/migrations`
|
|
46
|
+
- `npm run db:seed`: seed database from `app/db/seed.js`
|
|
47
|
+
- `npm run validate`: run full quality gate (check/build/bench/compat/db/export)
|
|
48
|
+
- `npm run smoke:dev`: automated SSR/API/auth/middleware smoke test
|
|
49
|
+
- `npm run smoke:start`: production `fastscript start` smoke test
|
|
50
|
+
- `npm run test:core`: middleware/auth/db/migration round-trip tests
|
|
51
|
+
- `npm run bench:report`: writes benchmark report to `benchmarks/latest-report.md`
|
|
52
|
+
- `npm run qa:all`: full quality sweep in one command
|
|
53
|
+
- `npm run worker`: run queue worker runtime
|
|
54
|
+
- `npm run deploy:*`: generate deploy adapters for node/vercel/cloudflare
|
|
55
|
+
- `npm run release:*`: semver bump + changelog append
|
|
56
|
+
- `npm run pack:check`: npm publish dry-run
|
|
57
|
+
|
|
58
|
+
## Additional Docs
|
|
59
|
+
|
|
60
|
+
- `docs/AI_CONTEXT_PACK_V1.md`
|
|
61
|
+
- `docs/PLUGIN_API_CONTRACT.md`
|
|
62
|
+
- `docs/INCIDENT_PLAYBOOK.md`
|
|
63
|
+
- `docs/DEPLOY_GUIDE.md`
|
|
64
|
+
|
|
65
|
+
## Project layout
|
|
66
|
+
|
|
67
|
+
```txt
|
|
68
|
+
app/
|
|
69
|
+
pages/
|
|
70
|
+
_layout.fs
|
|
71
|
+
index.fs
|
|
72
|
+
404.fs
|
|
73
|
+
api/
|
|
74
|
+
hello.js
|
|
75
|
+
auth.js
|
|
76
|
+
db/
|
|
77
|
+
migrations/
|
|
78
|
+
001_init.js
|
|
79
|
+
seed.js
|
|
80
|
+
middleware.fs
|
|
81
|
+
styles.css
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Page contract
|
|
85
|
+
|
|
86
|
+
- `export default function Page(ctx) { return htmlString }`
|
|
87
|
+
- Optional `export async function load(ctx) { return data }`
|
|
88
|
+
- Optional method actions in page files: `POST/PUT/PATCH/DELETE`
|
|
89
|
+
- `.fs` supports lenient FastScript syntax such as `~state = value`
|
|
90
|
+
- Optional `export function hydrate({ root, ...ctx })` for client hydration
|
|
91
|
+
|
|
92
|
+
## Routing
|
|
93
|
+
|
|
94
|
+
- `app/pages/index.fs` or `index.js` -> `/`
|
|
95
|
+
- `app/pages/blog/index.fs` or `index.js` -> `/blog`
|
|
96
|
+
- `app/pages/blog/[slug].fs` or `[slug].js` -> `/blog/:slug`
|
|
97
|
+
- `app/pages/404.fs` or `404.js` -> not found view
|
|
98
|
+
- `app/pages/_layout.fs` or `_layout.js` -> global layout wrapper
|
|
99
|
+
|
|
100
|
+
## Why this reset
|
|
101
|
+
|
|
102
|
+
This repo was reset intentionally to rebuild from ground up around a JavaScript-first model with minimal syntax friction.
|
package/app/api/auth.js
ADDED
package/app/api/hello.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const schemas = {
|
|
2
|
+
POST: { key: "string", content: "string" }
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export async function POST(ctx) {
|
|
6
|
+
const body = await ctx.input.validateBody(schemas.POST);
|
|
7
|
+
const put = ctx.storage.put(body.key, Buffer.from(body.content, "utf8"));
|
|
8
|
+
return ctx.helpers.json({ ok: true, ...put, url: ctx.storage.url(body.key) });
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { verifyWebhookRequest } from "../../src/webhook.mjs";
|
|
2
|
+
|
|
3
|
+
export async function POST(ctx) {
|
|
4
|
+
const result = await verifyWebhookRequest(ctx.req, {
|
|
5
|
+
secret: process.env.WEBHOOK_SECRET || "dev-secret",
|
|
6
|
+
replayDir: ".fastscript"
|
|
7
|
+
});
|
|
8
|
+
if (!result.ok) return ctx.helpers.json({ ok: false, reason: result.reason }, 401);
|
|
9
|
+
return ctx.helpers.json({ ok: true });
|
|
10
|
+
}
|
package/app/db/seed.js
ADDED
package/app/pages/404.fs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default function Layout({ content, pathname, user }) {
|
|
2
|
+
return `
|
|
3
|
+
<header class="nav">
|
|
4
|
+
<a href="/">FastScript</a>
|
|
5
|
+
<nav>
|
|
6
|
+
<a href="/">Home</a>
|
|
7
|
+
<a href="/docs">Docs</a>
|
|
8
|
+
<a href="/benchmarks">Benchmarks</a>
|
|
9
|
+
<a href="/showcase">Showcase</a>
|
|
10
|
+
<a href="/private">Private</a>
|
|
11
|
+
</nav>
|
|
12
|
+
<small>${user ? "Signed in" : "Guest"}</small>
|
|
13
|
+
</header>
|
|
14
|
+
<main class="page">${content}</main>
|
|
15
|
+
<footer class="footer">Built with FastScript • JS-first • .fs-native</footer>
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function Benchmarks() {
|
|
2
|
+
return `
|
|
3
|
+
<section>
|
|
4
|
+
<p class="eyebrow">Benchmarks</p>
|
|
5
|
+
<h1>Real budgets for 3G-first apps</h1>
|
|
6
|
+
<div class="grid">
|
|
7
|
+
<article><h3>JS first load</h3><p><strong>1.43KB</strong> gzip (budget 30KB)</p></article>
|
|
8
|
+
<article><h3>CSS first load</h3><p><strong>0.38KB</strong> gzip (budget 10KB)</p></article>
|
|
9
|
+
<article><h3>Build speed</h3><p>Sub-second warm build target</p></article>
|
|
10
|
+
<article><h3>Quality</h3><p>validate + smoke + tests</p></article>
|
|
11
|
+
</div>
|
|
12
|
+
<p>Report generated by <code>npm run bench:report</code>.</p>
|
|
13
|
+
</section>
|
|
14
|
+
`;
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default function DocsIndex() {
|
|
2
|
+
return `
|
|
3
|
+
<section>
|
|
4
|
+
<p class="eyebrow">Docs</p>
|
|
5
|
+
<h1>FastScript Documentation</h1>
|
|
6
|
+
<ul>
|
|
7
|
+
<li><code>pages/</code> for file-based routing</li>
|
|
8
|
+
<li><code>api/</code> for server endpoints</li>
|
|
9
|
+
<li><code>middleware.fs</code> for guards/security</li>
|
|
10
|
+
<li><code>db/</code> for migrations and seeds</li>
|
|
11
|
+
<li><code>jobs/</code> for worker handlers</li>
|
|
12
|
+
</ul>
|
|
13
|
+
<p>Quality gate: <code>npm run qa:all</code></p>
|
|
14
|
+
</section>
|
|
15
|
+
`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default function Home() {
|
|
2
|
+
return `
|
|
3
|
+
<section class="hero">
|
|
4
|
+
<p class="eyebrow">FastScript v0.1</p>
|
|
5
|
+
<h1>Full-stack speed with readable .fs syntax.</h1>
|
|
6
|
+
<p>SSR, API routes, auth, DB, jobs, storage, and deploy adapters in one stack.</p>
|
|
7
|
+
<div class="hero-links">
|
|
8
|
+
<a href="/docs">Read docs</a>
|
|
9
|
+
<a href="/benchmarks">See benchmarks</a>
|
|
10
|
+
<a href="/showcase">View showcase</a>
|
|
11
|
+
</div>
|
|
12
|
+
</section>
|
|
13
|
+
`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function hydrate({ root }) {
|
|
17
|
+
for (const a of root.querySelectorAll('.hero-links a')) {
|
|
18
|
+
a.style.transition = 'opacity .2s ease';
|
|
19
|
+
a.addEventListener('mouseenter', () => (a.style.opacity = '0.8'));
|
|
20
|
+
a.addEventListener('mouseleave', () => (a.style.opacity = '1'));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default function PrivatePage({ user }) {
|
|
2
|
+
return `<section><h1>Private</h1><p>Hello ${user?.name ?? "anonymous"}</p></section>`;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export async function GET(ctx) {
|
|
6
|
+
try {
|
|
7
|
+
const user = ctx.auth.requireUser();
|
|
8
|
+
return ctx.helpers.json({ ok: true, user });
|
|
9
|
+
} catch {
|
|
10
|
+
return ctx.helpers.redirect("/");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default function Showcase() {
|
|
2
|
+
return `
|
|
3
|
+
<section>
|
|
4
|
+
<p class="eyebrow">Showcase</p>
|
|
5
|
+
<h1>What you can build with FastScript</h1>
|
|
6
|
+
<div class="grid">
|
|
7
|
+
<article><h3>Starter app</h3><p>SSR + hydration + actions.</p></article>
|
|
8
|
+
<article><h3>Commerce API</h3><p>Orders endpoint + queue job email.</p></article>
|
|
9
|
+
<article><h3>Secure webhook</h3><p>Signature verify + replay protection.</p></article>
|
|
10
|
+
<article><h3>Portable exports</h3><p>.fs -> .js/.ts with one command.</p></article>
|
|
11
|
+
</div>
|
|
12
|
+
</section>
|
|
13
|
+
`;
|
|
14
|
+
}
|
package/app/styles.css
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
:root { color-scheme: dark; }
|
|
2
|
+
* { box-sizing: border-box; }
|
|
3
|
+
body { margin: 0; font: 16px/1.7 ui-sans-serif, system-ui; background: #050505; color: #fff; }
|
|
4
|
+
.nav { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 14px 24px; border-bottom: 1px solid #1f1f1f; position: sticky; top: 0; background: rgba(5,5,5,.9); backdrop-filter: blur(6px); }
|
|
5
|
+
.nav nav a { color: #d3d3ff; text-decoration: none; margin-right: 12px; }
|
|
6
|
+
.page { max-width: 1080px; margin: 0 auto; padding: 44px 24px; }
|
|
7
|
+
.hero h1, h1 { font-size: clamp(2rem, 5vw, 3.6rem); line-height: 1.07; margin: 0 0 12px; }
|
|
8
|
+
.eyebrow { color: #9f92ff; font-size: 12px; text-transform: uppercase; letter-spacing: .12em; }
|
|
9
|
+
.hero-links { display: flex; gap: 12px; margin-top: 16px; }
|
|
10
|
+
.hero-links a { color: #fff; border: 1px solid #353535; padding: 8px 12px; border-radius: 10px; text-decoration: none; }
|
|
11
|
+
.grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px; }
|
|
12
|
+
.grid article { border: 1px solid #202020; border-radius: 12px; padding: 14px; background: #090909; }
|
|
13
|
+
.footer { border-top: 1px solid #1f1f1f; padding: 24px; color: #8a8a8a; }
|
|
14
|
+
@media (max-width: 800px) { .grid { grid-template-columns: 1fr; } .nav { flex-wrap: wrap; } }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# FastScript AI Context Pack v1
|
|
2
|
+
|
|
3
|
+
## Core Contracts
|
|
4
|
+
- `.fs` primary, `.js` compatible.
|
|
5
|
+
- Route pages live in `app/pages`.
|
|
6
|
+
- API routes live in `app/api`.
|
|
7
|
+
- Optional `app/middleware.fs` for global middleware.
|
|
8
|
+
- Optional `load(ctx)` and HTTP methods (`GET/POST/PUT/PATCH/DELETE`).
|
|
9
|
+
|
|
10
|
+
## Validation
|
|
11
|
+
- Use `ctx.input.validateBody(schema)` and `ctx.input.validateQuery(schema)`.
|
|
12
|
+
- Use `schemas` export in route modules to auto-enforce request shape.
|
|
13
|
+
|
|
14
|
+
## Runtime
|
|
15
|
+
- SSR + hydration (`export function hydrate({ root })`).
|
|
16
|
+
- Queue available via `ctx.queue`.
|
|
17
|
+
- DB available via `ctx.db`.
|
|
18
|
+
- Auth available via `ctx.auth`.
|
|
19
|
+
|
|
20
|
+
## Quality Gates
|
|
21
|
+
- `npm run validate`
|
|
22
|
+
- `npm run test:core`
|
|
23
|
+
- `npm run smoke:dev`
|
|
24
|
+
- `npm run smoke:start`
|
|
25
|
+
- `npm run qa:all`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# FastScript Deploy Guide
|
|
2
|
+
|
|
3
|
+
## Node/PM2
|
|
4
|
+
- `npm run build`
|
|
5
|
+
- `npm run deploy:node`
|
|
6
|
+
- `pm2 start ecosystem.config.cjs`
|
|
7
|
+
|
|
8
|
+
## Vercel
|
|
9
|
+
- `npm run deploy:vercel`
|
|
10
|
+
- import project in Vercel
|
|
11
|
+
|
|
12
|
+
## Cloudflare
|
|
13
|
+
- `npm run deploy:cloudflare`
|
|
14
|
+
- `wrangler deploy`
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# FastScript Incident Playbook
|
|
2
|
+
|
|
3
|
+
## Rollback
|
|
4
|
+
1. Keep previous `dist/` artifact.
|
|
5
|
+
2. Stop current process.
|
|
6
|
+
3. Start previous artifact with `fastscript start`.
|
|
7
|
+
|
|
8
|
+
## Session Key Rotation
|
|
9
|
+
1. Set new `SESSION_SECRET`.
|
|
10
|
+
2. Restart app.
|
|
11
|
+
3. Existing sessions are invalidated.
|
|
12
|
+
|
|
13
|
+
## Backup
|
|
14
|
+
- Back up `.fastscript/` database and job files every hour.
|
|
15
|
+
|
|
16
|
+
## Verification
|
|
17
|
+
- Run `npm run smoke:start`.
|
|
18
|
+
- Check logs for `request_error` and high 5xx rate.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# FastScript Interop Rules
|
|
2
|
+
|
|
3
|
+
1. Prefer ESM imports.
|
|
4
|
+
2. CJS is supported via esbuild bundling.
|
|
5
|
+
3. `.fs` can import `.js/.mjs/.cjs/.json`.
|
|
6
|
+
4. `importAny()` handles default-only modules.
|
|
7
|
+
5. Use `resolveExport(mod, ["named", "default"])` for unknown module shapes.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# FastScript Plugin API Contract
|
|
2
|
+
|
|
3
|
+
## Hook Surface
|
|
4
|
+
- `middleware(ctx, next)`
|
|
5
|
+
- `onBuildStart(ctx)`
|
|
6
|
+
- `onBuildEnd(ctx)`
|
|
7
|
+
- `onRequestStart(ctx)`
|
|
8
|
+
- `onRequestEnd(ctx)`
|
|
9
|
+
|
|
10
|
+
## Plugin Shape
|
|
11
|
+
```js
|
|
12
|
+
export default {
|
|
13
|
+
name: "my-plugin",
|
|
14
|
+
setup(api) {
|
|
15
|
+
api.hooks.middleware(async (ctx, next) => next());
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Stability
|
|
21
|
+
- Contract version: `1`.
|
|
22
|
+
- Breaking changes require major version bump.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = { apps: [{ name: "fastscript-app", script: "node", args: "./src/cli.mjs start", env: { NODE_ENV: "production", PORT: 4173 } }] };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const schemas = { POST: { sku: "string", qty: "int" } };
|
|
2
|
+
export async function POST(ctx) {
|
|
3
|
+
const body = await ctx.input.validateBody(schemas.POST);
|
|
4
|
+
const order = { id: Date.now().toString(36), ...body };
|
|
5
|
+
ctx.db.collection("orders").set(order.id, order);
|
|
6
|
+
ctx.queue.enqueue("send-order-email", { orderId: order.id });
|
|
7
|
+
return ctx.helpers.json({ ok: true, order });
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function Layout({ content }) { return `<main>${content}</main>`; }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const schemas = { POST: { productId: "string", qty: "int" } };
|
|
2
|
+
|
|
3
|
+
export async function POST(ctx) {
|
|
4
|
+
const body = await ctx.input.validateBody(schemas.POST);
|
|
5
|
+
const cart = ctx.db.collection("carts");
|
|
6
|
+
const id = `c_${Date.now()}`;
|
|
7
|
+
cart.set(id, { id, ...body });
|
|
8
|
+
return ctx.helpers.json({ ok: true, id });
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export async function POST(ctx) {
|
|
2
|
+
const items = ctx.db.collection("carts").all();
|
|
3
|
+
const total = items.reduce((s, i) => s + i.qty * 10, 0);
|
|
4
|
+
const id = `o_${Date.now()}`;
|
|
5
|
+
ctx.db.collection("orders").set(id, { id, total, status: "paid" });
|
|
6
|
+
ctx.queue.enqueue("send-receipt", { orderId: id });
|
|
7
|
+
return ctx.helpers.json({ ok: true, orderId: id, total });
|
|
8
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export async function up(db) {
|
|
2
|
+
const products = db.collection("products");
|
|
3
|
+
if (!products.get("p1")) products.set("p1", { id: "p1", name: "Starter Plan", price: 19 });
|
|
4
|
+
if (!products.get("p2")) products.set("p2", { id: "p2", name: "Growth Plan", price: 49 });
|
|
5
|
+
if (!products.get("p3")) products.set("p3", { id: "p3", name: "Scale Plan", price: 99 });
|
|
6
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export async function load(ctx) {
|
|
2
|
+
const orders = ctx.db.collection("orders").all();
|
|
3
|
+
return { orders };
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export default function Dashboard({ orders }) {
|
|
7
|
+
const rows = (orders || []).map((o) => `<li>${o.id} • ${o.total} • ${o.status}</li>`).join("");
|
|
8
|
+
return `<section><h1>Dashboard</h1><ul>${rows}</ul></section>`;
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export async function load(ctx) {
|
|
2
|
+
const products = ctx.db.collection("products").all();
|
|
3
|
+
return { products };
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export default function Home({ products }) {
|
|
7
|
+
const cards = (products || []).map((p) => `<article style="border:1px solid #ddd;padding:12px;border-radius:10px"><h3>${p.name}</h3><p>$${p.price}</p><button data-add="${p.id}">Add to cart</button></article>`).join("");
|
|
8
|
+
return `<section><h1>Startup MVP Store</h1><div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px">${cards}</div><p><a href="/dashboard">Dashboard</a></p></section>`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function hydrate({ root }) {
|
|
12
|
+
for (const b of root.querySelectorAll('[data-add]')) {
|
|
13
|
+
b.addEventListener('click', async () => {
|
|
14
|
+
await fetch('/api/cart', { method:'POST', headers:{'content-type':'application/json'}, body: JSON.stringify({ productId: b.getAttribute('data-add'), qty: 1 })});
|
|
15
|
+
alert('Added to cart');
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fastscript",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JavaScript-first full-stack framework that is simpler and faster.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fastscript": "./src/cli.mjs"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "node ./src/cli.mjs dev",
|
|
11
|
+
"start": "node ./src/cli.mjs start",
|
|
12
|
+
"build": "node ./src/cli.mjs build",
|
|
13
|
+
"create": "node ./src/cli.mjs create",
|
|
14
|
+
"check": "node ./src/cli.mjs check",
|
|
15
|
+
"migrate": "node ./src/cli.mjs migrate",
|
|
16
|
+
"bench": "node ./src/cli.mjs bench",
|
|
17
|
+
"export:js": "node ./src/cli.mjs export --to js --out exported-js-app",
|
|
18
|
+
"export:ts": "node ./src/cli.mjs export --to ts --out exported-ts-app",
|
|
19
|
+
"compat": "node ./src/cli.mjs compat",
|
|
20
|
+
"validate": "node ./src/cli.mjs validate",
|
|
21
|
+
"db:migrate": "node ./src/cli.mjs db:migrate",
|
|
22
|
+
"db:seed": "node ./src/cli.mjs db:seed",
|
|
23
|
+
"deploy:node": "node ./src/cli.mjs deploy --target node",
|
|
24
|
+
"deploy:vercel": "node ./src/cli.mjs deploy --target vercel",
|
|
25
|
+
"deploy:cloudflare": "node ./src/cli.mjs deploy --target cloudflare",
|
|
26
|
+
"worker": "node ./src/cli.mjs worker",
|
|
27
|
+
"smoke:dev": "node ./scripts/smoke-dev.mjs",
|
|
28
|
+
"smoke:start": "node ./scripts/smoke-start.mjs",
|
|
29
|
+
"test:middleware": "node ./scripts/test-middleware.mjs",
|
|
30
|
+
"test:auth": "node ./scripts/test-auth.mjs",
|
|
31
|
+
"test:db": "node ./scripts/test-db.mjs",
|
|
32
|
+
"test:roundtrip": "node ./scripts/test-roundtrip.mjs",
|
|
33
|
+
"test:validation": "node ./scripts/test-validation.mjs",
|
|
34
|
+
"test:webhook-storage": "node ./scripts/test-webhook-storage.mjs",
|
|
35
|
+
"test:jobs": "node ./scripts/test-jobs.mjs",
|
|
36
|
+
"bench:report": "node ./scripts/bench-report.mjs",
|
|
37
|
+
"test:core": "npm run test:middleware && npm run test:auth && npm run test:db && npm run test:validation && npm run test:webhook-storage && npm run test:jobs && npm run test:roundtrip",
|
|
38
|
+
"qa:all": "npm run validate && npm run test:core && npm run smoke:dev && npm run smoke:start && npm run bench:report",
|
|
39
|
+
"release:patch": "node ./scripts/release.mjs patch",
|
|
40
|
+
"release:minor": "node ./scripts/release.mjs minor",
|
|
41
|
+
"release:major": "node ./scripts/release.mjs major",
|
|
42
|
+
"pack:check": "npm pack --dry-run"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20.0.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"esbuild": "^0.25.11"
|
|
49
|
+
}
|
|
50
|
+
}
|