@ingram-tech/nk-dev 0.2.0 → 0.2.4

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 CHANGED
@@ -15,7 +15,7 @@ vitest, jsdom, jest-dom, knip) as hard dependencies — so one install gives you
15
15
  the whole stack instead of re-listing each tool per site.
16
16
 
17
17
  > **Runtime vs dev-time.** nk-dev is the *dev-time* bundle. Runtime features
18
- > (`@ingram-tech/email`, `nk-db`, `nk-auth`, …) stay separate packages that
18
+ > (`@ingram-tech/nk-email`, `nk-db`, `nk-auth`, …) stay separate packages that
19
19
  > peer-depend on `next`/`react`. See the dev-toolchain carve-out in
20
20
  > [`philosophy.md`](https://github.com/ingram-technologies/nextkit/blob/main/docs/philosophy.md).
21
21
 
@@ -34,7 +34,7 @@ bun install # the prepare script wires the git hook
34
34
  | `.oxlintrc.json` | `extends` the shared oxlint rules (relative path — oxlint doesn't resolve package specifiers) |
35
35
  | `.oxfmtrc.json` | a copy of the house format config (oxfmt has no `extends`) |
36
36
  | `tsconfig.json` | `extends` `@ingram-tech/nk-dev/tsconfig/nextjs.json` + the site's own `include`/`paths` |
37
- | `knip.json` | seed config ignoring `@ingram-tech/nk-dev` (knip has no shareable config) |
37
+ | `knip.json` | seed config (knip has no shareable config): gates on dependency/file hygiene, with unused exports/types off (noisy); ignores `@ingram-tech/nk-dev` |
38
38
  | `.githooks/pre-commit` + `prepare` script | oxfmt format-on-commit |
39
39
  | `CLAUDE.md` | the agent-guide `@import` |
40
40
 
package/guide.md CHANGED
@@ -9,8 +9,11 @@ package. Stay a thin, standard Next.js app (bun · oxlint + oxfmt · strict TS).
9
9
  - **Any form that emails or stores a submission MUST use `@ingram-tech/bot-protection`**
10
10
  (server: `verifyHuman` → silently drop bots; client: honeypot + signed token).
11
11
  Never ship a form without it.
12
- - **Send email only via `@ingram-tech/email`** — never add another mail client.
13
- - Format/lint with **oxlint + oxfmt** via `nk` (`@ingram-tech/nk-cli`); don't
12
+ - **Send email only via `@ingram-tech/nk-email`** — never add another mail client.
13
+ - **Never trust an external request body's shape validate it with Zod, never
14
+ `as`-cast it.** Every `/api` route and webhook handler takes untrusted input;
15
+ an `as` cast is a lie the type-checker can't catch at runtime.
16
+ - Format/lint with **oxlint + oxfmt** via `nk` (`@ingram-tech/nk-dev`); don't
14
17
  reintroduce ESLint, nor Prettier for code (`nk` uses Prettier only for SQL,
15
18
  which oxfmt can't format). `nk` is optional convenience that only orchestrates
16
19
  the standard tools — the site must stay buildable with plain `next build` / `next dev`.
@@ -48,13 +51,33 @@ if your frontend fetches it as an API, it's **`/api/…`**; if a provider, cron,
48
51
  queue calls it, it's **`/internal/…`**. Never put OAuth callbacks or webhooks in
49
52
  the UI/page tree, and never expose internal plumbing under `/api/`.
50
53
 
54
+ ## Data & migrations
55
+
56
+ - **IDs are UUIDv7** — never UUIDv4 / `gen_random_uuid()` / `defaultRandom()` /
57
+ nanoids. UUIDv7 is time-ordered, so it keeps index locality instead of
58
+ fragmenting the B-tree on random inserts, and one uniform id format spans every
59
+ table. On Postgres ≥18 the column default is native `uuidv7()`
60
+ (`uuid("id").primaryKey().default(sql\`uuidv7()\`)`); set Better Auth
61
+ `advanced.database.generateId: false` so the DB — not Better Auth's JS nanoid —
62
+ mints ids. Ids that cross a **public contract** are skinned to `prefix_base58`
63
+ via `@ingram-tech/nk-db/id` (`createIdRegistry`) — never expose a raw UUID.
64
+ External ids you don't mint (Stripe `cus_`, OAuth) stay `text`.
65
+ - **Migrations don't auto-apply on deploy.** Code ships ahead of the prod schema
66
+ unless someone runs the migration against the target DB — a page that reads a
67
+ newly-added column 500s in prod until then. Apply migrations with
68
+ `@ingram-tech/nk-db`'s drift-aware runner (`@ingram-tech/nk-db/migrate`), which
69
+ surfaces the real Postgres error and pre-flights journal drift. Generate **and
70
+ apply** in the same step; don't leave "run the migration" as a handoff.
71
+
51
72
  ## What nextkit provides (reach for these)
52
73
 
53
- - `@ingram-tech/email` — Cloudflare email: `sendEmail`, `fromAddress`
54
- - `@ingram-tech/nk-auth` — Better Auth foundation: presets you spread into your own `betterAuth()` (mounts at `/auth` via `authBasePath`; org / JWT / passkey / pool / client helpers)
55
- - `@ingram-tech/nk-db` — Postgres data layer: `createPool` (one TLS-aware pool) + `createQueries` (raw SQL) + `createDb` (Drizzle), plus a PGlite dev/test harness at `@ingram-tech/nk-db/pglite`
74
+ - `@ingram-tech/nk-email` — Cloudflare email: `sendEmail`, `fromAddress`
75
+ - `@ingram-tech/nk-auth` — Better Auth foundation: presets you spread into your own `betterAuth()` (mounts at `/auth` via `authBasePath`; org / JWT / passkey / pool / client helpers). Don't hand-roll session reads or auth middleware — bind `createAuthHelpers` (`getUser` / `requireUser` / `redirectIfAuthenticated`, from `@ingram-tech/nk-auth/server`) and gate routes with the loop-safe `createAuthMiddleware`
76
+ - `@ingram-tech/nk-db` — Postgres data layer: `createPool` (one TLS-aware pool) + `createQueries` (raw SQL) + `createDb` (Drizzle), the PGlite dev/test harness at `@ingram-tech/nk-db/pglite`, the prefixed-id codec at `@ingram-tech/nk-db/id`, and the drift-aware migration runner at `@ingram-tech/nk-db/migrate`
77
+ - `@ingram-tech/nk-api` — the standard HTTP API seam (Hono + `@hono/zod-openapi`): one `{ error, details? }` envelope, `createApiApp` / `createRouter`, auth + multi-tenant resource-scope middleware, pagination helpers, and an emitted OpenAPI/Swagger doc. Reach for it instead of hand-rolling route handlers
78
+ - `@ingram-tech/nk-billing` — Stripe primitives: subscriptions, a Stripe-side wallet, and an optional Postgres credit ledger behind the `/credits` subpath. Prices resolve at runtime by Stripe `lookup_key` — **never hardcode a price id**, so test and live share one code path
56
79
  - `@ingram-tech/bot-protection` — invisible form protection (honeypot + timing + Vercel BotID)
57
- - `@ingram-tech/newsletter` — Supabase newsletter: subscribe / send, 1-click unsubscribe
80
+ - `@ingram-tech/nk-i18n` — type-safe, English-as-key i18n: the English source text *is* the key (no `en.json`), ICU MessageFormat, colocated JSON catalogs; routing is left to the site
58
81
  - `@ingram-tech/nk-dev` — the whole dev toolchain in one devDependency: the `nk` command (`nk dev` boots local PGlite via `@ingram-tech/nk-db` if installed, then Next; plus `nk format` / `lint` / `knip` / `check` / `type-check` / `build`), the shared oxlint + oxfmt / TypeScript / Vitest config, knip, the oxfmt format-on-commit hook, and this guide. `nk check` runs every fast checker (oxlint, oxfmt, SQL, knip) in one gate. `nk init` scaffolds a site to use it all.
59
82
 
60
83
  For detail on any package, read its README in `node_modules/@ingram-tech/<pkg>/`.
package/lib/dev.js CHANGED
@@ -26,8 +26,8 @@ function hasPgliteDev() {
26
26
  * (static/marketing sites with no database). PGlite logic lives in nk-db, not
27
27
  * here — `nk` only orchestrates.
28
28
  *
29
- * `nk dev` does not boot local Supabase; the fleet has moved off it. The few
30
- * Supabase-Postgres holdouts start it themselves until they migrate.
29
+ * `nk dev` boots no external database service when nk-db is installed it runs
30
+ * local PGlite (Postgres-in-WASM), and a site with no database just runs Next.
31
31
  */
32
32
  export function dev(extraArgs = []) {
33
33
  const command = hasPgliteDev()
package/lib/format.js CHANGED
@@ -8,14 +8,14 @@ import { run } from "./run.js";
8
8
  const require = createRequire(import.meta.url);
9
9
 
10
10
  // House SQL defaults, used only when the site has no Prettier config of its own
11
- // (matches the house tab style + Supabase's Postgres dialect).
11
+ // (matches the house tab style + the PostgreSQL dialect).
12
12
  const SQL_DEFAULTS = { useTabs: true, language: "postgresql" };
13
13
 
14
14
  /**
15
15
  * `nk format` / `nk format --check`.
16
16
  *
17
17
  * Code (JS/TS/JSON/CSS) goes through oxfmt; SQL goes through Prettier, which
18
- * oxfmt can't format. Prettier + prettier-plugin-sql are bundled with nk-cli,
18
+ * oxfmt can't format. Prettier + prettier-plugin-sql are bundled with nk-dev,
19
19
  * so they never appear in any app's dependencies — the "no Prettier for code"
20
20
  * rule still holds, it's just the one file type oxfmt lacks.
21
21
  */
package/lib/init.js CHANGED
@@ -50,14 +50,27 @@ const TSCONFIG = {
50
50
 
51
51
  const VITEST_HINT = `import { mergeConfig } from "vitest/config";\\nimport { nextkitTestConfig } from "@ingram-tech/nk-dev/vitest";\\nexport default mergeConfig(nextkitTestConfig, {});`;
52
52
 
53
- // knip has no shareable config, so each site carries its own. This seed ignores
54
- // @ingram-tech/nk-dev: a site that runs raw tools (not the `nk` bin) gives knip
55
- // no way to see nk-dev as used, so without this it'd fail as an unused
56
- // dependency. (Sites that do call `nk` in scripts can drop it — knip will hint.)
57
- // Add `entry`/`ignore` as the project grows.
53
+ // knip has no shareable config, so each site carries its own seed. The house
54
+ // policy: gate on dependency/file hygiene (unused files/deps, unlisted,
55
+ // unresolved) its low-false-positive checks and turn OFF unused
56
+ // exports/types, which are noisy and usually intentional API surface. Run an
57
+ // export-cleanup pass by flipping those back on when you want it.
58
+ //
59
+ // ignoreDependencies keeps @ingram-tech/nk-dev: knip doesn't follow the
60
+ // relative-path `extends` in .oxlintrc/tsconfig, so a site that doesn't call the
61
+ // `nk` bin gives knip no way to see nk-dev as used. Add `entry`/`ignore` as the
62
+ // project grows (e.g. `scripts/**`, a self-contained `pulumi/**`).
58
63
  const KNIP = {
59
64
  $schema: "https://unpkg.com/knip@6/schema.json",
60
65
  ignoreDependencies: ["@ingram-tech/nk-dev"],
66
+ rules: {
67
+ exports: "off",
68
+ types: "off",
69
+ nsExports: "off",
70
+ nsTypes: "off",
71
+ enumMembers: "off",
72
+ duplicates: "off",
73
+ },
61
74
  };
62
75
 
63
76
  const PRE_COMMIT = `#!/bin/sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingram-tech/nk-dev",
3
- "version": "0.2.0",
3
+ "version": "0.2.4",
4
4
  "description": "The nextkit dev toolchain in one package: the `nk` CLI plus shared oxlint/oxfmt, TypeScript, and Vitest config, the format-on-commit hook, and the AI agent guide. `nk init` scaffolds a site to use it.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/tier-b.json CHANGED
@@ -5,10 +5,6 @@
5
5
  "error",
6
6
  {
7
7
  "paths": [
8
- {
9
- "name": "@supabase/supabase-js",
10
- "message": "Golden-path data access uses @ingram-tech/nk-db (direct pg + Drizzle), not supabase-js."
11
- },
12
8
  {
13
9
  "name": "pg",
14
10
  "importNames": ["Pool", "Client"],
@@ -4,8 +4,14 @@
4
4
  "compilerOptions": {
5
5
  "target": "ESNext",
6
6
  "lib": ["esnext"],
7
- "module": "esnext",
8
- "moduleResolution": "bundler",
7
+ // NodeNext so published packages ("type": "module") emit real Node ESM and
8
+ // tsc ENFORCES explicit .js extensions on relative imports (TS2835).
9
+ // "bundler" silently tolerates extensionless imports and emits them
10
+ // verbatim — invalid under Node ESM / Turbopack, a recurring break source.
11
+ // App consumers override back to "bundler" in nextjs.json (Next resolves
12
+ // modules itself and must not require .js extensions in app source).
13
+ "module": "nodenext",
14
+ "moduleResolution": "nodenext",
9
15
  "allowJs": true,
10
16
  "skipLibCheck": true,
11
17
  "strict": true,
@@ -4,6 +4,11 @@
4
4
  "extends": "./base.json",
5
5
  "compilerOptions": {
6
6
  "lib": ["dom", "dom.iterable", "esnext"],
7
+ // Next apps let the bundler resolve modules — keep "bundler" here so app
8
+ // source needn't use .js import extensions. (base.json is "nodenext" for
9
+ // published packages; this override insulates apps from that.)
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
7
12
  "jsx": "preserve",
8
13
  "noEmit": true,
9
14
  "declaration": false,