create-nifra 2.2.0 → 2.3.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/README.md +4 -4
- package/dist/agent-files.d.ts +14 -14
- package/dist/agent-files.js +29 -29
- package/dist/agents.d.ts +4 -4
- package/dist/agents.js +63 -63
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +8 -8
- package/dist/cli.d.ts +6 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +56 -35
- package/dist/cli.js.map +1 -1
- package/dist/db.d.ts +6 -6
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +138 -41
- package/dist/db.js.map +1 -1
- package/dist/scaffold/features.d.ts +41 -0
- package/dist/scaffold/features.d.ts.map +1 -0
- package/dist/scaffold/features.js +48 -0
- package/dist/scaffold/features.js.map +1 -0
- package/dist/scaffold/frameworks.d.ts +73 -0
- package/dist/scaffold/frameworks.d.ts.map +1 -0
- package/dist/scaffold/frameworks.js +100 -0
- package/dist/scaffold/frameworks.js.map +1 -0
- package/dist/scaffold/site-build.d.ts +60 -0
- package/dist/scaffold/site-build.d.ts.map +1 -0
- package/dist/scaffold/site-build.js +198 -0
- package/dist/scaffold/site-build.js.map +1 -0
- package/dist/scaffold/site-files.d.ts +16 -0
- package/dist/scaffold/site-files.d.ts.map +1 -0
- package/dist/scaffold/site-files.js +112 -0
- package/dist/scaffold/site-files.js.map +1 -0
- package/dist/scaffold/site.d.ts +33 -0
- package/dist/scaffold/site.d.ts.map +1 -0
- package/dist/scaffold/site.js +99 -0
- package/dist/scaffold/site.js.map +1 -0
- package/package.json +2 -2
- package/src/agent-files.ts +29 -29
- package/template/README.md +3 -3
- package/template/nifra.assurance.ts +80 -0
- package/template/package.json +5 -3
- package/template/src/app.ts +10 -11
- package/template/src/routes.ts +13 -0
- package/template-isr/README.md +1 -1
- package/template-isr/backend.ts +11 -15
- package/template-isr/nifra.assurance.ts +81 -0
- package/template-isr/package.json +8 -7
- package/template-isr/page.ts +12 -0
- package/template-isr/routes/_404.tsx +1 -1
- package/template-isr/routes/_layout.tsx +1 -1
- package/template-isr/routes/index.tsx +2 -2
- package/template-isr/server.ts +1 -1
- package/template-isr/worker.ts +2 -2
- package/template-isr/wrangler.toml +2 -2
- package/template-site/Dockerfile +1 -1
- package/template-site/README.md +6 -6
- package/template-site/backend.ts +11 -13
- package/template-site/counter.ts +16 -0
- package/template-site/nifra.assurance.ts +83 -0
- package/template-site/nifra.config.ts +1 -1
- package/template-site/routes/index.tsx +1 -1
- package/template-site/server-node.ts +1 -1
- package/template-site/server-vercel.ts +1 -1
- package/template-site-preact/README.md +7 -7
- package/template-site-preact/nifra.config.ts +1 -1
- package/template-site-preact/routes/index.tsx +1 -1
- package/template-site-solid/README.md +7 -7
- package/template-site-solid/nifra.config.ts +2 -2
- package/template-site-solid/routes/index.tsx +1 -1
- package/template-site-svelte/README.md +7 -7
- package/template-site-svelte/nifra.config.ts +2 -2
- package/template-site-svelte/routes/_layout.svelte +1 -1
- package/template-site-svelte/routes/index.svelte +1 -1
- package/template-site-vue/README.md +7 -7
- package/template-site-vue/nifra.config.ts +2 -2
- package/template-site-vue/routes/_layout.vue +2 -2
- package/template-site-vue/routes/index.vue +3 -3
- package/template-site/build-bun.ts +0 -27
- package/template-site/build-deno.ts +0 -25
- package/template-site/build-node.ts +0 -26
- package/template-site/build-vercel.ts +0 -45
- package/template-site/build.ts +0 -29
- package/template-site/framework.ts +0 -6
- package/template-site/package.json +0 -38
- package/template-site/tsconfig.json +0 -37
- package/template-site-preact/.dockerignore +0 -14
- package/template-site-preact/Dockerfile +0 -20
- package/template-site-preact/_worker.ts +0 -17
- package/template-site-preact/backend.ts +0 -15
- package/template-site-preact/build-bun.ts +0 -27
- package/template-site-preact/build-deno.ts +0 -25
- package/template-site-preact/build-node.ts +0 -26
- package/template-site-preact/build-vercel.ts +0 -45
- package/template-site-preact/build.ts +0 -29
- package/template-site-preact/deno.json +0 -7
- package/template-site-preact/framework.ts +0 -6
- package/template-site-preact/gitignore +0 -16
- package/template-site-preact/package.json +0 -35
- package/template-site-preact/server-bun.ts +0 -35
- package/template-site-preact/server-deno.ts +0 -38
- package/template-site-preact/server-node.ts +0 -40
- package/template-site-preact/server-vercel.ts +0 -17
- package/template-site-preact/tsconfig.json +0 -38
- package/template-site-preact/wrangler.toml +0 -4
- package/template-site-solid/.dockerignore +0 -14
- package/template-site-solid/Dockerfile +0 -20
- package/template-site-solid/_worker.ts +0 -17
- package/template-site-solid/backend.ts +0 -15
- package/template-site-solid/build-bun.ts +0 -33
- package/template-site-solid/build-deno.ts +0 -32
- package/template-site-solid/build-node.ts +0 -33
- package/template-site-solid/build-vercel.ts +0 -47
- package/template-site-solid/build.ts +0 -36
- package/template-site-solid/deno.json +0 -7
- package/template-site-solid/framework.ts +0 -6
- package/template-site-solid/gitignore +0 -16
- package/template-site-solid/package.json +0 -35
- package/template-site-solid/server-bun.ts +0 -35
- package/template-site-solid/server-deno.ts +0 -38
- package/template-site-solid/server-node.ts +0 -40
- package/template-site-solid/server-vercel.ts +0 -17
- package/template-site-solid/tsconfig.json +0 -38
- package/template-site-solid/wrangler.toml +0 -4
- package/template-site-svelte/.dockerignore +0 -14
- package/template-site-svelte/Dockerfile +0 -20
- package/template-site-svelte/_worker.ts +0 -17
- package/template-site-svelte/backend.ts +0 -15
- package/template-site-svelte/build-bun.ts +0 -32
- package/template-site-svelte/build-deno.ts +0 -31
- package/template-site-svelte/build-node.ts +0 -32
- package/template-site-svelte/build-vercel.ts +0 -46
- package/template-site-svelte/build.ts +0 -35
- package/template-site-svelte/deno.json +0 -7
- package/template-site-svelte/framework.ts +0 -6
- package/template-site-svelte/gitignore +0 -16
- package/template-site-svelte/package.json +0 -35
- package/template-site-svelte/server-bun.ts +0 -35
- package/template-site-svelte/server-deno.ts +0 -38
- package/template-site-svelte/server-node.ts +0 -40
- package/template-site-svelte/server-vercel.ts +0 -17
- package/template-site-svelte/tsconfig.json +0 -37
- package/template-site-svelte/wrangler.toml +0 -4
- package/template-site-vue/.dockerignore +0 -14
- package/template-site-vue/Dockerfile +0 -20
- package/template-site-vue/_worker.ts +0 -17
- package/template-site-vue/backend.ts +0 -15
- package/template-site-vue/build-bun.ts +0 -37
- package/template-site-vue/build-deno.ts +0 -36
- package/template-site-vue/build-node.ts +0 -37
- package/template-site-vue/build-vercel.ts +0 -54
- package/template-site-vue/build.ts +0 -40
- package/template-site-vue/deno.json +0 -7
- package/template-site-vue/framework.ts +0 -6
- package/template-site-vue/gitignore +0 -16
- package/template-site-vue/package.json +0 -36
- package/template-site-vue/server-bun.ts +0 -35
- package/template-site-vue/server-deno.ts +0 -38
- package/template-site-vue/server-node.ts +0 -40
- package/template-site-vue/server-vercel.ts +0 -17
- package/template-site-vue/tsconfig.json +0 -37
- package/template-site-vue/wrangler.toml +0 -4
package/dist/db.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `--db <preset>`
|
|
2
|
+
* `--db <preset>` - wire a Drizzle data layer into a freshly scaffolded app, so a human (or a coding
|
|
3
3
|
* agent) starts from a correct, production-grade DB setup instead of inventing one. nifra ships NO DB
|
|
4
|
-
* abstraction (that belongs to Drizzle/libSQL, not a web framework)
|
|
4
|
+
* abstraction (that belongs to Drizzle/libSQL, not a web framework) - this just bundles the existing
|
|
5
5
|
* tool, the Redwood-style "batteries available" move: a starter schema, a typed client, a migration
|
|
6
6
|
* config, scripts, and env, all rip-out-able.
|
|
7
7
|
*
|
|
8
8
|
* Three presets, by runtime fit:
|
|
9
|
-
* - drizzle-libsql
|
|
9
|
+
* - drizzle-libsql - SQLite that runs EVERYWHERE incl. the edge (local file, or Turso). The default
|
|
10
10
|
* cross-runtime answer.
|
|
11
|
-
* - drizzle-postgres
|
|
12
|
-
* - drizzle-sqlite
|
|
11
|
+
* - drizzle-postgres - Postgres (postgres.js) on Bun/Node/Deno.
|
|
12
|
+
* - drizzle-sqlite - Bun's built-in `bun:sqlite` (sync, local file). Bun-only.
|
|
13
13
|
*
|
|
14
14
|
* The starter `notes` table follows the production-grade DB defaults for its dialect; what a dialect
|
|
15
15
|
* can't express inline (CHECK constraints, RLS, uuidv7) is called out in a comment, per those rules.
|
|
@@ -50,12 +50,12 @@ export const notes = pgTable("notes", {
|
|
|
50
50
|
export type Note = typeof notes.$inferSelect
|
|
51
51
|
export type NewNote = typeof notes.$inferInsert
|
|
52
52
|
`;
|
|
53
|
-
// SQLite has no UUID/TIMESTAMPTZ/RLS
|
|
53
|
+
// SQLite has no UUID/TIMESTAMPTZ/RLS - so: text id (app-generated UUID), millisecond-epoch timestamps
|
|
54
54
|
// (timestamp_ms mode → JS Date), NOT NULL by default, soft-delete via deleted_at. Shared by the
|
|
55
55
|
// bun:sqlite and libSQL presets (same dialect). CHECK constraints are deferred to the migration.
|
|
56
56
|
const SQLITE_SCHEMA = `import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
|
|
57
57
|
|
|
58
|
-
// Production-grade starter table (SQLite dialect
|
|
58
|
+
// Production-grade starter table (SQLite dialect - see the comment in your DB defaults for what the
|
|
59
59
|
// dialect can't enforce inline: CHECK constraints go in the generated migration).
|
|
60
60
|
export const notes = sqliteTable("notes", {
|
|
61
61
|
id: text("id")
|
|
@@ -77,12 +77,79 @@ export type NewNote = typeof notes.$inferInsert
|
|
|
77
77
|
`;
|
|
78
78
|
// --- typed clients (export \`db\` + re-export the schema) --------------------------------------------
|
|
79
79
|
// Each prints the same usage header so an agent sees how to wire it (decorate) and query it.
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
//
|
|
80
|
+
// The connection is not imported by route modules directly. \`read.ts\` and \`write.ts\` sit in front of
|
|
81
|
+
// it, and routes import whichever one they need - which is what lets a GET route declare \`db.read\`
|
|
82
|
+
// and nothing else. See the header those files carry for why the split is load-bearing.
|
|
83
|
+
const WIRE_DOC = `// The connection. Route modules do NOT import this file: they import \`./read.ts\` or \`./write.ts\`, so
|
|
84
|
+
// what a route can reach matches what it declares. \`db/read-routes.ts\` shows the shape, and merging it
|
|
85
|
+
// is one line:
|
|
86
|
+
// import { notesRead } from "../db/read-routes.ts"
|
|
87
|
+
// export const app = server().merge(notesRead)
|
|
88
|
+
// Never top-level-import any of this into a routes/ page file (server-only) - reach it via ctx.api.`;
|
|
89
|
+
/**
|
|
90
|
+
* Why the seam is split by access rather than exported as one `db`.
|
|
91
|
+
*
|
|
92
|
+
* `nifra check` works out what a route can reach from the module that REGISTERS it, following its
|
|
93
|
+
* imports. A route may not reach further than it declares, and a GET route may not declare a domain
|
|
94
|
+
* write at all - so a GET registered in a module that can reach a write has no legal declaration and
|
|
95
|
+
* has to be moved. Splitting reads from writes at the seam, and again at the routes, is what keeps
|
|
96
|
+
* every route's reach equal to what it says about itself.
|
|
97
|
+
*
|
|
98
|
+
* The same shape is what makes the policy in `nifra.assurance.ts` bite: `db/write.ts` grants
|
|
99
|
+
* `db.write`, the `authenticated-write` rule demands authentication of anything holding it, and a POST
|
|
100
|
+
* that skips auth fails the check rather than shipping.
|
|
101
|
+
*/
|
|
102
|
+
const SEAM_DOC = (access) => `// The ${access} half of the database seam. Routes that ${access === "read" ? "only read" : "write"} import THIS, never \`./index.ts\` -
|
|
103
|
+
// a module that can reach both is a module whose GET routes cannot declare what they reach. Keep the
|
|
104
|
+
// two halves import-disjoint and every route's declaration stays honest.`;
|
|
105
|
+
const DRIZZLE_READ = `${SEAM_DOC("read")}
|
|
106
|
+
import { isNull } from "drizzle-orm"
|
|
107
|
+
import { db } from "./index.ts"
|
|
108
|
+
import { type Note, notes } from "./schema.ts"
|
|
109
|
+
|
|
110
|
+
export const listNotes = async (): Promise<Note[]> =>
|
|
111
|
+
db.select().from(notes).where(isNull(notes.deletedAt))
|
|
112
|
+
`;
|
|
113
|
+
const DRIZZLE_WRITE = `${SEAM_DOC("write")}
|
|
114
|
+
import { db } from "./index.ts"
|
|
115
|
+
import { type NewNote, type Note, notes } from "./schema.ts"
|
|
116
|
+
|
|
117
|
+
export const createNote = async (note: NewNote): Promise<Note | undefined> =>
|
|
118
|
+
(await db.insert(notes).values(note).returning())[0]
|
|
119
|
+
`;
|
|
120
|
+
const READ_ROUTES = `import { server } from "@nifrajs/core/server"
|
|
121
|
+
import { t } from "@nifrajs/schema"
|
|
122
|
+
import { listNotes } from "./read.ts"
|
|
123
|
+
|
|
124
|
+
// Registered here rather than in your app module, because reach is per-module: this file can see the
|
|
125
|
+
// read half of the seam and nothing else, so \`db.read\` is the whole truth about it. Merge it with
|
|
126
|
+
// \`app.merge(notesRead)\`.
|
|
127
|
+
//
|
|
128
|
+
// The path is \`/db/notes\` rather than \`/notes\` on purpose. This module ships UNMERGED as a sample, and
|
|
129
|
+
// \`nifra check\` associates modules with routes by matching the registered PATH across your source - so
|
|
130
|
+
// a sample sharing a path with a route your template already registers would lend it this file's
|
|
131
|
+
// capability reach and fail the check on a route that never touches the database. Rename it to
|
|
132
|
+
// whatever you like once you merge it; just do not collide with a path already in the app.
|
|
133
|
+
export const notesRead = server().get(
|
|
134
|
+
"/db/notes",
|
|
135
|
+
{
|
|
136
|
+
capabilities: ["db.read"],
|
|
137
|
+
response: t.array(t.object({ id: t.string(), title: t.string(), body: t.string() })),
|
|
138
|
+
},
|
|
139
|
+
async () => (await listNotes()).map(({ id, title, body }) => ({ id, title, body })),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
// Writes go in a SIBLING file that imports \`./write.ts\`, never this one:
|
|
143
|
+
//
|
|
144
|
+
// export const notesWrite = server().post(
|
|
145
|
+
// "/notes",
|
|
146
|
+
// { body: NoteInput, capabilities: ["db.write"] },
|
|
147
|
+
// async (c) => createNote(c.body),
|
|
148
|
+
// )
|
|
149
|
+
//
|
|
150
|
+
// Expect it to fail \`nifra assure\` until it is authenticated - the starter policy requires proof of
|
|
151
|
+
// who asked before anything writes business state. Scaffold auth with \`--auth\`.
|
|
152
|
+
`;
|
|
86
153
|
const PG_CLIENT = `import { drizzle } from "drizzle-orm/postgres-js"
|
|
87
154
|
import postgres from "postgres"
|
|
88
155
|
import * as schema from "./schema.ts"
|
|
@@ -91,10 +158,10 @@ ${WIRE_DOC}
|
|
|
91
158
|
|
|
92
159
|
const url = process.env.DATABASE_URL
|
|
93
160
|
if (url === undefined || url === "") {
|
|
94
|
-
throw new Error("DATABASE_URL is not set
|
|
161
|
+
throw new Error("DATABASE_URL is not set - copy .env.example to .env and fill it in.")
|
|
95
162
|
}
|
|
96
163
|
|
|
97
|
-
// One pooled client per process (per instance in a multi-instance deploy
|
|
164
|
+
// One pooled client per process (per instance in a multi-instance deploy - fine for Postgres).
|
|
98
165
|
export const db = drizzle(postgres(url), { schema })
|
|
99
166
|
export * from "./schema.ts"
|
|
100
167
|
`;
|
|
@@ -104,7 +171,7 @@ import * as schema from "./schema.ts"
|
|
|
104
171
|
|
|
105
172
|
${WIRE_DOC}
|
|
106
173
|
|
|
107
|
-
// Bun's built-in SQLite
|
|
174
|
+
// Bun's built-in SQLite - sync + fast, local file. (Bun-only; for Node/Deno/edge use the libsql preset.)
|
|
108
175
|
export const db = drizzle(new Database(process.env.DATABASE_URL ?? "local.db"), { schema })
|
|
109
176
|
export * from "./schema.ts"
|
|
110
177
|
`;
|
|
@@ -114,7 +181,7 @@ import * as schema from "./schema.ts"
|
|
|
114
181
|
|
|
115
182
|
${WIRE_DOC}
|
|
116
183
|
|
|
117
|
-
// libSQL runs everywhere
|
|
184
|
+
// libSQL runs everywhere - a local file (file:local.db), a remote Turso URL (libsql://…) with an auth
|
|
118
185
|
// token, or an embedded replica. The one SQLite client that also works on the edge (Cloudflare Workers).
|
|
119
186
|
const token = process.env.DATABASE_AUTH_TOKEN
|
|
120
187
|
export const db = drizzle(
|
|
@@ -172,7 +239,7 @@ DATABASE_URL="file:local.db"
|
|
|
172
239
|
# DATABASE_AUTH_TOKEN="your-token"
|
|
173
240
|
`;
|
|
174
241
|
// --- Prisma presets ---------------------------------------------------------------------------------
|
|
175
|
-
// Prisma owns its schema DSL (prisma/schema.prisma), client codegen, and migration engine
|
|
242
|
+
// Prisma owns its schema DSL (prisma/schema.prisma), client codegen, and migration engine - so the
|
|
176
243
|
// scaffold is schema + a singleton client + the prisma scripts (no drizzle-kit equivalent needed).
|
|
177
244
|
const PRISMA_CLIENT_VERSION = "^6.5.0";
|
|
178
245
|
const PRISMA_SCRIPTS = {
|
|
@@ -213,7 +280,7 @@ generator client {
|
|
|
213
280
|
provider = "prisma-client-js"
|
|
214
281
|
}
|
|
215
282
|
|
|
216
|
-
// Production-grade starter model (SQLite dialect
|
|
283
|
+
// Production-grade starter model (SQLite dialect - no native UUID/TIMESTAMPTZ/RLS; Prisma stores the
|
|
217
284
|
// String id + DateTime stamps for you). NOT NULL by default, soft-delete via deletedAt. CHECK
|
|
218
285
|
// constraints are deferred to the generated migration SQL.
|
|
219
286
|
model Note {
|
|
@@ -227,14 +294,20 @@ model Note {
|
|
|
227
294
|
@@map("notes")
|
|
228
295
|
}
|
|
229
296
|
`;
|
|
297
|
+
const PRISMA_READ = `${SEAM_DOC("read")}
|
|
298
|
+
import { db } from "./index.ts"
|
|
299
|
+
|
|
300
|
+
export const listNotes = async () => db.note.findMany({ where: { deletedAt: null } })
|
|
301
|
+
`;
|
|
302
|
+
const PRISMA_WRITE = `${SEAM_DOC("write")}
|
|
303
|
+
import { db } from "./index.ts"
|
|
304
|
+
|
|
305
|
+
export const createNote = async (note: { title: string; body?: string }) =>
|
|
306
|
+
db.note.create({ data: note })
|
|
307
|
+
`;
|
|
230
308
|
const PRISMA_CLIENT = `import { PrismaClient } from "@prisma/client"
|
|
231
309
|
|
|
232
|
-
|
|
233
|
-
// import { server } from "@nifrajs/core/server"
|
|
234
|
-
// import { db } from "./db"
|
|
235
|
-
// export const app = server().decorate("db", db)
|
|
236
|
-
// .get("/notes", async (c) => c.db.note.findMany({ where: { deletedAt: null } }))
|
|
237
|
-
// Never top-level-import this into a routes/ page file (server-only) — reach it via c.db / ctx.api.
|
|
310
|
+
${WIRE_DOC}
|
|
238
311
|
|
|
239
312
|
// One PrismaClient per process. The globalThis guard stops dev hot-reload from opening a new connection
|
|
240
313
|
// pool on every reload (which exhausts the DB); production gets a single fresh instance.
|
|
@@ -274,24 +347,30 @@ export interface DB {
|
|
|
274
347
|
notes: NotesTable
|
|
275
348
|
}
|
|
276
349
|
`;
|
|
350
|
+
const KYSELY_READ = `${SEAM_DOC("read")}
|
|
351
|
+
import { db } from "./index.ts"
|
|
352
|
+
|
|
353
|
+
export const listNotes = async () =>
|
|
354
|
+
db.selectFrom("notes").selectAll().where("deleted_at", "is", null).execute()
|
|
355
|
+
`;
|
|
356
|
+
const KYSELY_WRITE = `${SEAM_DOC("write")}
|
|
357
|
+
import { db } from "./index.ts"
|
|
358
|
+
|
|
359
|
+
export const createNote = async (note: { title: string; body?: string }) =>
|
|
360
|
+
db.insertInto("notes").values(note).returningAll().executeTakeFirst()
|
|
361
|
+
`;
|
|
277
362
|
const KYSELY_PG_CLIENT = `import { Kysely, PostgresDialect } from "kysely"
|
|
278
363
|
import { Pool } from "pg"
|
|
279
364
|
import type { DB } from "./schema.ts"
|
|
280
365
|
|
|
281
|
-
|
|
282
|
-
// import { server } from "@nifrajs/core/server"
|
|
283
|
-
// import { db } from "./db"
|
|
284
|
-
// export const app = server().decorate("db", db)
|
|
285
|
-
// .get("/notes", async (c) =>
|
|
286
|
-
// c.db.selectFrom("notes").selectAll().where("deleted_at", "is", null).execute())
|
|
287
|
-
// Never top-level-import this into a routes/ page file (server-only) — reach it via c.db / ctx.api.
|
|
366
|
+
${WIRE_DOC}
|
|
288
367
|
|
|
289
368
|
const url = process.env.DATABASE_URL
|
|
290
369
|
if (url === undefined || url === "") {
|
|
291
|
-
throw new Error("DATABASE_URL is not set
|
|
370
|
+
throw new Error("DATABASE_URL is not set - copy .env.example to .env and fill it in.")
|
|
292
371
|
}
|
|
293
372
|
|
|
294
|
-
// One pooled client per process (per instance in a multi-instance deploy
|
|
373
|
+
// One pooled client per process (per instance in a multi-instance deploy - fine for Postgres).
|
|
295
374
|
export const db = new Kysely<DB>({
|
|
296
375
|
dialect: new PostgresDialect({ pool: new Pool({ connectionString: url }) }),
|
|
297
376
|
})
|
|
@@ -299,7 +378,7 @@ export type { DB } from "./schema.ts"
|
|
|
299
378
|
`;
|
|
300
379
|
const KYSELY_MIGRATION = `import { type Kysely, sql } from "kysely"
|
|
301
380
|
|
|
302
|
-
// Production-grade starter table. Postgres: UUID PK (gen_random_uuid
|
|
381
|
+
// Production-grade starter table. Postgres: UUID PK (gen_random_uuid - swap to gen_uuidv7() for insert
|
|
303
382
|
// locality on hot tables once available), TIMESTAMPTZ stamps, NOT NULL by default, soft-delete via
|
|
304
383
|
// deleted_at. Add CHECK constraints (e.g. length caps) and RLS here per your DB defaults.
|
|
305
384
|
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
@@ -356,6 +435,9 @@ export const DB_PRESETS = {
|
|
|
356
435
|
files: {
|
|
357
436
|
"db/schema.ts": SQLITE_SCHEMA,
|
|
358
437
|
"db/index.ts": LIBSQL_CLIENT,
|
|
438
|
+
"db/read.ts": DRIZZLE_READ,
|
|
439
|
+
"db/write.ts": DRIZZLE_WRITE,
|
|
440
|
+
"db/read-routes.ts": READ_ROUTES,
|
|
359
441
|
"drizzle.config.ts": LIBSQL_CONFIG,
|
|
360
442
|
".env.example": LIBSQL_ENV,
|
|
361
443
|
},
|
|
@@ -364,13 +446,16 @@ export const DB_PRESETS = {
|
|
|
364
446
|
label: "Drizzle + Postgres",
|
|
365
447
|
orm: "drizzle",
|
|
366
448
|
dialect: "postgres",
|
|
367
|
-
note: "Postgres (postgres.js) on Bun/Node/Deno
|
|
449
|
+
note: "Postgres (postgres.js) on Bun/Node/Deno - not the edge.",
|
|
368
450
|
deps: { "drizzle-orm": DRIZZLE_ORM, postgres: "^3.4.5" },
|
|
369
451
|
devDeps: { "drizzle-kit": DRIZZLE_KIT },
|
|
370
452
|
scripts: SCRIPTS,
|
|
371
453
|
files: {
|
|
372
454
|
"db/schema.ts": PG_SCHEMA,
|
|
373
455
|
"db/index.ts": PG_CLIENT,
|
|
456
|
+
"db/read.ts": DRIZZLE_READ,
|
|
457
|
+
"db/write.ts": DRIZZLE_WRITE,
|
|
458
|
+
"db/read-routes.ts": READ_ROUTES,
|
|
374
459
|
"drizzle.config.ts": PG_CONFIG,
|
|
375
460
|
".env.example": PG_ENV,
|
|
376
461
|
},
|
|
@@ -379,13 +464,16 @@ export const DB_PRESETS = {
|
|
|
379
464
|
label: "Drizzle + SQLite (bun:sqlite)",
|
|
380
465
|
orm: "drizzle",
|
|
381
466
|
dialect: "sqlite",
|
|
382
|
-
note: "Bun's built-in SQLite
|
|
467
|
+
note: "Bun's built-in SQLite - local file, Bun-only.",
|
|
383
468
|
deps: { "drizzle-orm": DRIZZLE_ORM },
|
|
384
469
|
devDeps: { "drizzle-kit": DRIZZLE_KIT },
|
|
385
470
|
scripts: SCRIPTS,
|
|
386
471
|
files: {
|
|
387
472
|
"db/schema.ts": SQLITE_SCHEMA,
|
|
388
473
|
"db/index.ts": SQLITE_CLIENT,
|
|
474
|
+
"db/read.ts": DRIZZLE_READ,
|
|
475
|
+
"db/write.ts": DRIZZLE_WRITE,
|
|
476
|
+
"db/read-routes.ts": READ_ROUTES,
|
|
389
477
|
"drizzle.config.ts": SQLITE_CONFIG,
|
|
390
478
|
".env.example": SQLITE_ENV,
|
|
391
479
|
},
|
|
@@ -394,13 +482,16 @@ export const DB_PRESETS = {
|
|
|
394
482
|
label: "Prisma + Postgres",
|
|
395
483
|
orm: "prisma",
|
|
396
484
|
dialect: "postgres",
|
|
397
|
-
note: "Prisma ORM on Postgres (Bun/Node/Deno)
|
|
485
|
+
note: "Prisma ORM on Postgres (Bun/Node/Deno) - own schema DSL, migrate engine, and Studio.",
|
|
398
486
|
deps: { "@prisma/client": PRISMA_CLIENT_VERSION },
|
|
399
487
|
devDeps: { prisma: PRISMA_CLIENT_VERSION },
|
|
400
488
|
scripts: PRISMA_SCRIPTS,
|
|
401
489
|
files: {
|
|
402
490
|
"prisma/schema.prisma": PRISMA_PG_SCHEMA,
|
|
403
491
|
"db/index.ts": PRISMA_CLIENT,
|
|
492
|
+
"db/read.ts": PRISMA_READ,
|
|
493
|
+
"db/write.ts": PRISMA_WRITE,
|
|
494
|
+
"db/read-routes.ts": READ_ROUTES,
|
|
404
495
|
".env.example": PRISMA_PG_ENV,
|
|
405
496
|
},
|
|
406
497
|
},
|
|
@@ -408,13 +499,16 @@ export const DB_PRESETS = {
|
|
|
408
499
|
label: "Prisma + SQLite",
|
|
409
500
|
orm: "prisma",
|
|
410
501
|
dialect: "sqlite",
|
|
411
|
-
note: "Prisma ORM on a local SQLite file
|
|
502
|
+
note: "Prisma ORM on a local SQLite file - fastest start; migrate to Postgres later by swapping the datasource.",
|
|
412
503
|
deps: { "@prisma/client": PRISMA_CLIENT_VERSION },
|
|
413
504
|
devDeps: { prisma: PRISMA_CLIENT_VERSION },
|
|
414
505
|
scripts: PRISMA_SCRIPTS,
|
|
415
506
|
files: {
|
|
416
507
|
"prisma/schema.prisma": PRISMA_SQLITE_SCHEMA,
|
|
417
508
|
"db/index.ts": PRISMA_CLIENT,
|
|
509
|
+
"db/read.ts": PRISMA_READ,
|
|
510
|
+
"db/write.ts": PRISMA_WRITE,
|
|
511
|
+
"db/read-routes.ts": READ_ROUTES,
|
|
418
512
|
".env.example": PRISMA_SQLITE_ENV,
|
|
419
513
|
},
|
|
420
514
|
},
|
|
@@ -422,13 +516,16 @@ export const DB_PRESETS = {
|
|
|
422
516
|
label: "Kysely + Postgres",
|
|
423
517
|
orm: "kysely",
|
|
424
518
|
dialect: "postgres",
|
|
425
|
-
note: "Kysely typed query builder on Postgres (node-postgres)
|
|
519
|
+
note: "Kysely typed query builder on Postgres (node-postgres) - you own the schema types + migrations.",
|
|
426
520
|
deps: { kysely: KYSELY_VERSION, pg: "^8.13.1" },
|
|
427
521
|
devDeps: { "@types/pg": "^8.11.10" },
|
|
428
522
|
scripts: KYSELY_SCRIPTS,
|
|
429
523
|
files: {
|
|
430
524
|
"db/schema.ts": KYSELY_PG_SCHEMA,
|
|
431
525
|
"db/index.ts": KYSELY_PG_CLIENT,
|
|
526
|
+
"db/read.ts": KYSELY_READ,
|
|
527
|
+
"db/write.ts": KYSELY_WRITE,
|
|
528
|
+
"db/read-routes.ts": READ_ROUTES,
|
|
432
529
|
"db/migrate.ts": KYSELY_MIGRATE_RUNNER,
|
|
433
530
|
"db/migrations/0001_create_notes.ts": KYSELY_MIGRATION,
|
|
434
531
|
".env.example": PG_ENV,
|
|
@@ -458,7 +555,7 @@ export async function writeDbFiles(target, choice) {
|
|
|
458
555
|
current = await readFile(gitignorePath, "utf8");
|
|
459
556
|
}
|
|
460
557
|
catch {
|
|
461
|
-
// No .gitignore in this template
|
|
558
|
+
// No .gitignore in this template - create one with just the DB block.
|
|
462
559
|
}
|
|
463
560
|
if (!current.includes("local.db")) {
|
|
464
561
|
await writeFile(gitignorePath, `${current}${GITIGNORE_BLOCK}`);
|
package/dist/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB;IAChB,iBAAiB;IACjB,eAAe;IACf,iBAAiB;CACT,CAAA;AAsBV,MAAM,WAAW,GAAG,SAAS,CAAA;AAC7B,MAAM,WAAW,GAAG,SAAS,CAAA;AAC7B,MAAM,OAAO,GAAqC;IAChD,aAAa,EAAE,sBAAsB;IACrC,YAAY,EAAE,qBAAqB;IACnC,WAAW,EAAE,oBAAoB;CAClC,CAAA;AAED,uGAAuG;AAEvG,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;CAiBjB,CAAA;AAED,sGAAsG;AACtG,gGAAgG;AAChG,iGAAiG;AACjG,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;CAqBrB,CAAA;AAED,wGAAwG;AACxG,6FAA6F;AAE7F,MAAM,QAAQ,GAAG;;;;;qGAKoF,CAAA;AAErG,MAAM,SAAS,GAAG;;;;EAIhB,QAAQ;;;;;;;;;;CAUT,CAAA;AAED,MAAM,aAAa,GAAG;;;;EAIpB,QAAQ;;;;;CAKT,CAAA;AAED,MAAM,aAAa,GAAG;;;;EAIpB,QAAQ;;;;;;;;;;;;;CAaT,CAAA;AAED,uGAAuG;AAEvG,MAAM,SAAS,GAAG;;;;;;;;;CASjB,CAAA;AAED,MAAM,aAAa,GAAG;;;;;;;;CAQrB,CAAA;AAED,MAAM,aAAa,GAAG;;;;;;;;;;;CAWrB,CAAA;AAED,uGAAuG;AAEvG,MAAM,MAAM,GAAG;;CAEd,CAAA;AACD,MAAM,UAAU,GAAG;;CAElB,CAAA;AACD,MAAM,UAAU,GAAG;;;;;CAKlB,CAAA;AAED,uGAAuG;AACvG,mGAAmG;AACnG,mGAAmG;AAEnG,MAAM,qBAAqB,GAAG,QAAQ,CAAA;AACtC,MAAM,cAAc,GAAqC;IACvD,aAAa,EAAE,iBAAiB;IAChC,YAAY,EAAE,oBAAoB;IAClC,WAAW,EAAE,eAAe;CAC7B,CAAA;AAED,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuBxB,CAAA;AAED,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;CAsB5B,CAAA;AAED,MAAM,aAAa,GAAG
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a feature adds to a scaffold's manifest, and the one place those additions are merged.
|
|
3
|
+
*
|
|
4
|
+
* `--db`, `--auth`, `--deploy` and `--ci` each used to reach into the parsed `package.json` and spread
|
|
5
|
+
* themselves over it, in an order fixed by the line they appeared on. That works until two of them
|
|
6
|
+
* want the same key: the later spread wins, silently, and a preset that shadowed the scaffold's own
|
|
7
|
+
* `check` script would remove the assurance gate from every project scaffolded with it. No preset does
|
|
8
|
+
* that today - all six were checked - which is exactly when to put the rail in.
|
|
9
|
+
*
|
|
10
|
+
* So a contribution says what it adds, and REPLACING an existing key has to be declared. A collision
|
|
11
|
+
* nobody declared is an error rather than a last-writer-wins race between two option handlers.
|
|
12
|
+
*/
|
|
13
|
+
export interface FeatureContribution {
|
|
14
|
+
/** How the feature names itself in a collision error, e.g. `--db drizzle-libsql`. */
|
|
15
|
+
readonly label: string;
|
|
16
|
+
readonly dependencies?: Readonly<Record<string, string>>;
|
|
17
|
+
readonly devDependencies?: Readonly<Record<string, string>>;
|
|
18
|
+
readonly scripts?: Readonly<Record<string, string>>;
|
|
19
|
+
/**
|
|
20
|
+
* Keys this feature intends to overwrite. `--deploy` repoints `build` and `deploy` at the chosen
|
|
21
|
+
* target, which is the whole point of the flag - so it says so, and every other collision stays an
|
|
22
|
+
* error. The distinction is the feature: declared replacement is a decision, silent replacement is
|
|
23
|
+
* the bug this exists to catch.
|
|
24
|
+
*/
|
|
25
|
+
readonly replaces?: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
export interface ScaffoldManifest {
|
|
28
|
+
name?: string;
|
|
29
|
+
scripts?: Record<string, string>;
|
|
30
|
+
dependencies?: Record<string, string>;
|
|
31
|
+
devDependencies?: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Merge every feature's contribution into the scaffold's manifest.
|
|
35
|
+
*
|
|
36
|
+
* Applied in order, but the order no longer decides anything: a key two features both want, or a key a
|
|
37
|
+
* feature would take from the scaffold without saying so, throws naming both sides. That turns a
|
|
38
|
+
* question you would have to reason about into one the scaffolder answers.
|
|
39
|
+
*/
|
|
40
|
+
export declare function applyFeatures(manifest: ScaffoldManifest, contributions: readonly FeatureContribution[]): void;
|
|
41
|
+
//# sourceMappingURL=features.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/scaffold/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,mBAAmB;IAClC,qFAAqF;IACrF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxD,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzC;AAID;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,EAAE,SAAS,mBAAmB,EAAE,GAC5C,IAAI,CA4BN"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a feature adds to a scaffold's manifest, and the one place those additions are merged.
|
|
3
|
+
*
|
|
4
|
+
* `--db`, `--auth`, `--deploy` and `--ci` each used to reach into the parsed `package.json` and spread
|
|
5
|
+
* themselves over it, in an order fixed by the line they appeared on. That works until two of them
|
|
6
|
+
* want the same key: the later spread wins, silently, and a preset that shadowed the scaffold's own
|
|
7
|
+
* `check` script would remove the assurance gate from every project scaffolded with it. No preset does
|
|
8
|
+
* that today - all six were checked - which is exactly when to put the rail in.
|
|
9
|
+
*
|
|
10
|
+
* So a contribution says what it adds, and REPLACING an existing key has to be declared. A collision
|
|
11
|
+
* nobody declared is an error rather than a last-writer-wins race between two option handlers.
|
|
12
|
+
*/
|
|
13
|
+
const SECTIONS = ["scripts", "dependencies", "devDependencies"];
|
|
14
|
+
/**
|
|
15
|
+
* Merge every feature's contribution into the scaffold's manifest.
|
|
16
|
+
*
|
|
17
|
+
* Applied in order, but the order no longer decides anything: a key two features both want, or a key a
|
|
18
|
+
* feature would take from the scaffold without saying so, throws naming both sides. That turns a
|
|
19
|
+
* question you would have to reason about into one the scaffolder answers.
|
|
20
|
+
*/
|
|
21
|
+
export function applyFeatures(manifest, contributions) {
|
|
22
|
+
/** Who last wrote each key, so a collision can name the other side rather than just the winner. */
|
|
23
|
+
const owner = new Map();
|
|
24
|
+
for (const section of SECTIONS) {
|
|
25
|
+
for (const key of Object.keys(manifest[section] ?? {}))
|
|
26
|
+
owner.set(`${section}.${key}`, "the scaffold");
|
|
27
|
+
}
|
|
28
|
+
for (const contribution of contributions) {
|
|
29
|
+
const declared = new Set(contribution.replaces ?? []);
|
|
30
|
+
for (const section of SECTIONS) {
|
|
31
|
+
const additions = contribution[section];
|
|
32
|
+
if (additions === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
manifest[section] ??= {};
|
|
35
|
+
const target = manifest[section];
|
|
36
|
+
for (const [key, value] of Object.entries(additions)) {
|
|
37
|
+
const previous = owner.get(`${section}.${key}`);
|
|
38
|
+
if (previous !== undefined && !declared.has(key) && target[key] !== value) {
|
|
39
|
+
throw new Error(`${contribution.label} would overwrite ${section}.${key} from ${previous}. ` +
|
|
40
|
+
"Declare it in `replaces` if that is intended.");
|
|
41
|
+
}
|
|
42
|
+
target[key] = value;
|
|
43
|
+
owner.set(`${section}.${key}`, contribution.label);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=features.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.js","sourceRoot":"","sources":["../../src/scaffold/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwBH,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,cAAc,EAAE,iBAAiB,CAAU,CAAA;AAExE;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,QAA0B,EAC1B,aAA6C;IAE7C,mGAAmG;IACnG,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACpD,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,GAAG,EAAE,EAAE,cAAc,CAAC,CAAA;IAClD,CAAC;IAED,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAA;QACrD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;YACvC,IAAI,SAAS,KAAK,SAAS;gBAAE,SAAQ;YACrC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;YACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAA;YAChC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,GAAG,EAAE,CAAC,CAAA;gBAC/C,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;oBAC1E,MAAM,IAAI,KAAK,CACb,GAAG,YAAY,CAAC,KAAK,oBAAoB,OAAO,IAAI,GAAG,SAAS,QAAQ,IAAI;wBAC1E,+CAA+C,CAClD,CAAA;gBACH,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;gBACnB,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,GAAG,EAAE,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;YACpD,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a frontend framework contributes to a site scaffold.
|
|
3
|
+
*
|
|
4
|
+
* The five site templates were five directories of 26 files each, and 13 of those 26 were
|
|
5
|
+
* byte-identical in all five - the Dockerfile, every server entry, the worker, the assurance config.
|
|
6
|
+
* Editing the runtime story meant editing it five times and hoping. This session made the same
|
|
7
|
+
* one-line `package.json` edit in eight files, which is the whole argument.
|
|
8
|
+
*
|
|
9
|
+
* So the runtime knowledge lives once (see `site.ts`), and a framework contributes only what is
|
|
10
|
+
* genuinely its own: an adapter, a bundler plugin, the resolve conditions and defines its compiler
|
|
11
|
+
* needs, and its dependency set. The prose a framework needs to explain ITSELF - why Solid wants a
|
|
12
|
+
* `"solid"` resolve condition, what `@preact/preset-vite` is - stays in a real file next to it. That
|
|
13
|
+
* is the line: mechanical differences are modelled, explanations are written.
|
|
14
|
+
*/
|
|
15
|
+
/** A `plugins: [x("dom")]` entry the framework's compiler needs in a Bun build. */
|
|
16
|
+
export interface BunPluginSpec {
|
|
17
|
+
/** Module the plugin factory is imported from. */
|
|
18
|
+
readonly specifier: string;
|
|
19
|
+
/** The factory's exported name. */
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/** Trailing comment on the call, when the framework has something to say about it. */
|
|
22
|
+
readonly note?: {
|
|
23
|
+
readonly dom: string;
|
|
24
|
+
readonly ssr: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface FrameworkSpec {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
/** The adapter export, e.g. `reactAdapter`. */
|
|
30
|
+
readonly adapter: string;
|
|
31
|
+
/** The adapter's package, e.g. `@nifrajs/web-react`. */
|
|
32
|
+
readonly package: string;
|
|
33
|
+
/** Compiler plugin for `.vue`/`.svelte`/Solid JSX. Absent for the JSX-native frameworks. */
|
|
34
|
+
readonly bunPlugin?: BunPluginSpec;
|
|
35
|
+
/** Resolve conditions this framework adds beyond the runtime's own. */
|
|
36
|
+
readonly conditions?: {
|
|
37
|
+
readonly client?: readonly string[];
|
|
38
|
+
readonly ssr?: readonly string[];
|
|
39
|
+
};
|
|
40
|
+
/** Build-time defines beyond `process.env.NODE_ENV`, in emission order. */
|
|
41
|
+
readonly define?: Readonly<Record<string, string>>;
|
|
42
|
+
/** Comment above the hoisted `define`, when the framework's flags need explaining. */
|
|
43
|
+
readonly defineNote?: string;
|
|
44
|
+
/** Runtime dependencies beyond the shared Nifra set, in emission order. */
|
|
45
|
+
readonly runtimeDependencies: Readonly<Record<string, string>>;
|
|
46
|
+
/** Dev dependencies beyond `@nifrajs/cli`, `typescript` and `vite`, in emission order. */
|
|
47
|
+
readonly devDependencies: Readonly<Record<string, string>>;
|
|
48
|
+
/** What this framework needs from `tsconfig.json`. */
|
|
49
|
+
readonly typescript: {
|
|
50
|
+
readonly jsx?: string;
|
|
51
|
+
readonly jsxImportSource?: string;
|
|
52
|
+
readonly types?: readonly string[];
|
|
53
|
+
/** False when routes are not `.tsx` - Svelte's are `.svelte`, so the glob would match nothing. */
|
|
54
|
+
readonly includeTsx?: boolean;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The `@nifrajs/*` range a scaffolded site installs.
|
|
59
|
+
*
|
|
60
|
+
* One constant, because it used to be a regex sweep over eight `package.json` files in the release
|
|
61
|
+
* script with nothing checking the result - and the script's own comment warns that a missed bump
|
|
62
|
+
* ships templates installing the PREVIOUS release. `scaffold-version.test.ts` now fails when this
|
|
63
|
+
* drifts from what core is publishing, so the footgun is a red test rather than a silent regression.
|
|
64
|
+
*/
|
|
65
|
+
export declare const NIFRA_DEP_RANGE = "^2.3.0";
|
|
66
|
+
/**
|
|
67
|
+
* React is first because it is the default (`--framework` omitted scaffolds it), and because the
|
|
68
|
+
* other four are most easily read as deltas from it.
|
|
69
|
+
*/
|
|
70
|
+
export declare const FRAMEWORK_SPECS: Readonly<Record<string, FrameworkSpec>>;
|
|
71
|
+
export declare const FRAMEWORK_IDS: string[];
|
|
72
|
+
export declare function frameworkSpec(id: string): FrameworkSpec;
|
|
73
|
+
//# sourceMappingURL=frameworks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frameworks.d.ts","sourceRoot":"","sources":["../../src/scaffold/frameworks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,mCAAmC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/D;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC,uEAAuE;IACvE,QAAQ,CAAC,UAAU,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAA;IAC/F,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAClD,sFAAsF;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC9D,0FAA0F;IAC1F,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC1D,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;QACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QAClC,kGAAkG;QAClG,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAC9B,CAAA;CACF;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,WAAW,CAAA;AAEvC;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAgEnE,CAAA;AAED,eAAO,MAAM,aAAa,UAA+B,CAAA;AAEzD,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,CAMvD"}
|