create-kumiko-app 0.4.138 → 0.4.140

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "source": "samples/apps/use-all-bundled APP_FEATURES (composeFeatures includeBundled)",
3
- "featureCount": 55,
3
+ "featureCount": 56,
4
4
  "features": [
5
5
  {
6
6
  "name": "admin-shell",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  {
50
50
  "name": "auth-email-password",
51
- "description": "Provides email+password authentication: the always-on handlers are `login`, `changePassword`, and `logout`; optional flows — password reset, email verification, magic-link self-signup, and tenant invite — are registered only when you pass their respective option objects (`passwordReset`, `emailVerification`, `signup`, `invite`) to `createAuthEmailPasswordFeature(opts)`. All four magic-link flows (reset, verification, signup activation, tenant invite) dispatch their mail through the `delivery` feature via `ctx.notify`, so mounting any of them additionally requires `delivery`. Tokens are HMAC-signed (reset/verify) or opaque-random in Redis (signup/invite). Requires the `user` and `tenant` features, and declares `JWT_SECRET` (≥ 32 chars) in `authEmailPasswordEnvSchema` so a missing secret surfaces at boot validation rather than on the first login attempt.",
51
+ "description": "Provides email+password authentication: the always-on handlers are `login`, `changePassword`, and `logout`; optional flows — password reset, email verification, magic-link self-signup, tenant invite, and account-unlock — are registered only when you pass their respective option objects (`passwordReset`, `emailVerification`, `signup`, `invite`, `accountUnlock`) to `createAuthEmailPasswordFeature(opts)`. All five magic-link flows dispatch their mail through the `delivery` feature via `ctx.notify`, so mounting any of them additionally requires `delivery`. Tokens are HMAC-signed (reset/verify/unlock) or opaque-random in Redis (signup/invite). `accountUnlock` is the self-service escape hatch for `accountLockout`'s monotonic failure-counter (#1266): confirming the mailed token clears the Redis lockout state without touching the user entity. Requires the `user` and `tenant` features, and declares `JWT_SECRET` (≥ 32 chars) in `authEmailPasswordEnvSchema` so a missing secret surfaces at boot validation rather than on the first login attempt.",
52
52
  "toggleableDefault": null,
53
53
  "requires": [
54
54
  "user",
@@ -94,10 +94,35 @@
94
94
  "label": "Tenant-Invite-Flow",
95
95
  "type": "boolean",
96
96
  "default": false
97
+ },
98
+ {
99
+ "key": "accountUnlock",
100
+ "label": "Account-Unlock-Flow",
101
+ "type": "boolean",
102
+ "default": false
97
103
  }
98
104
  ]
99
105
  }
100
106
  },
107
+ {
108
+ "name": "auth-foundation",
109
+ "description": "Declares the `tokenVerifier` (Bearer-token auth) and `sessionStore` extension points. Provider-features register a static `shape` plus a `build()` for tokenVerifier — mount together with at least one auth-provider-* feature (e.g. personal-access-tokens) and call `resolveTokenVerifier(deps, rawToken)` to find the matching one; sessions self-registers as the single sessionStore provider.",
110
+ "toggleableDefault": null,
111
+ "requires": [],
112
+ "optionalRequires": [],
113
+ "configReads": [],
114
+ "exposesApis": [],
115
+ "usesApis": [],
116
+ "extensionsUsed": [],
117
+ "configKeys": [],
118
+ "secrets": [],
119
+ "writeHandlers": [],
120
+ "uiHints": {
121
+ "displayLabel": "Auth Provider Foundation",
122
+ "category": "identity",
123
+ "recommended": false
124
+ }
125
+ },
101
126
  {
102
127
  "name": "auth-mfa",
103
128
  "description": "TOTP-based two-factor authentication: enable/disable flow with QR-code setup, 8 single-use recovery codes, and (once wired into a login flow) a second login step after password verification. Secrets are envelope-encrypted at rest via the same MasterKeyProvider as `secrets`/`config`.",
@@ -1320,17 +1345,23 @@
1320
1345
  },
1321
1346
  {
1322
1347
  "name": "personal-access-tokens",
1323
- "description": "Long-lived, revocable Personal Access Tokens for headless HTTP-API access. Stores SHA-256 token hashes in the `store_api_tokens` direct-write table; the plaintext is returned once at creation. `create`/`revoke`/`mine` manage a user's own tokens and `available-scopes` lists the app-declared scope catalog. Bearer tokens carrying the PAT prefix are resolved before jwt.verify (roles resolved live, granted scopes enforced fail-closed at the API boundary) — the resolver is wired via run-prod-app, not the dispatcher. Pass { toggleable: { default: false } } to tier-gate the whole feature.",
1348
+ "description": "Long-lived, revocable Personal Access Tokens for headless HTTP-API access. Stores SHA-256 token hashes in the `store_api_tokens` direct-write table; the plaintext is returned once at creation. `create`/`revoke`/`mine` manage a user's own tokens and `available-scopes` lists the app-declared scope catalog. Bearer tokens carrying the PAT prefix are resolved before jwt.verify (roles resolved live, granted scopes enforced fail-closed at the API boundary) — registered as an auth-foundation tokenVerifier provider, resolved generically by the middleware. Pass { toggleable: { default: false } } to tier-gate the whole feature.",
1324
1349
  "toggleableDefault": null,
1325
1350
  "requires": [
1326
1351
  "user",
1327
- "tenant"
1352
+ "tenant",
1353
+ "auth-foundation"
1328
1354
  ],
1329
1355
  "optionalRequires": [],
1330
1356
  "configReads": [],
1331
1357
  "exposesApis": [],
1332
1358
  "usesApis": [],
1333
- "extensionsUsed": [],
1359
+ "extensionsUsed": [
1360
+ {
1361
+ "extensionName": "tokenVerifier",
1362
+ "entityName": "pat"
1363
+ }
1364
+ ],
1334
1365
  "configKeys": [],
1335
1366
  "secrets": [],
1336
1367
  "writeHandlers": [
@@ -1597,10 +1628,11 @@
1597
1628
  },
1598
1629
  {
1599
1630
  "name": "sessions",
1600
- "description": "Tracks signed-in clients in the `store_user_sessions` table (one row per JWT, keyed by the `sid`/`jti` claim) and exposes handlers for `mine` (list your sessions), `revoke`, and `revokeAllOthers`. Session creation and revocation on the hot auth path are handled by `createSessionCallbacks()`, wired into `buildServer({ auth: { ... } })` outside the dispatcher; the feature also ships a manual-trigger cleanup job for pruning expired rows and an optional `autoRevokeOnPasswordChange` hook that mass-revokes all sessions for a user whenever their `passwordHash` changes.",
1631
+ "description": "Tracks signed-in clients in the `store_user_sessions` table (one row per JWT, keyed by the `sid`/`jti` claim) and exposes handlers for `mine` (list your sessions), `revoke`, and `revokeAllOthers`. Session creation and revocation on the hot auth path are handled by `createSessionCallbacks()`, wired into `buildServer({ auth: { ... } })` outside the dispatcher; the same callbacks are also registered as an auth-foundation sessionStore provider, resolvable generically via `resolveSessionStore()`. The feature also ships a manual-trigger cleanup job for pruning expired rows and an optional `autoRevokeOnPasswordChange` hook that mass-revokes all sessions for a user whenever their `passwordHash` changes.",
1601
1632
  "toggleableDefault": null,
1602
1633
  "requires": [
1603
- "user"
1634
+ "user",
1635
+ "auth-foundation"
1604
1636
  ],
1605
1637
  "optionalRequires": [],
1606
1638
  "configReads": [],
@@ -1608,7 +1640,12 @@
1608
1640
  "sessions.revokeAllForUser"
1609
1641
  ],
1610
1642
  "usesApis": [],
1611
- "extensionsUsed": [],
1643
+ "extensionsUsed": [
1644
+ {
1645
+ "extensionName": "sessionStore",
1646
+ "entityName": "default"
1647
+ }
1648
+ ],
1612
1649
  "configKeys": [],
1613
1650
  "secrets": [],
1614
1651
  "writeHandlers": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kumiko-app",
3
- "version": "0.4.138",
3
+ "version": "0.4.140",
4
4
  "description": "`bun create kumiko-app <name>` — scaffold a new Kumiko app with an interactive feature picker.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -30,9 +30,9 @@
30
30
  "vendor:manifest": "bun run scripts/vendor-manifest.ts"
31
31
  },
32
32
  "dependencies": {
33
- "@cosmicdrift/kumiko-dev-server": "0.158.2",
34
- "@cosmicdrift/kumiko-framework": "0.158.2",
35
- "@cosmicdrift/kumiko-server-runtime": "0.158.2",
33
+ "@cosmicdrift/kumiko-dev-server": "0.159.1",
34
+ "@cosmicdrift/kumiko-framework": "0.159.1",
35
+ "@cosmicdrift/kumiko-server-runtime": "0.159.1",
36
36
  "@inquirer/core": "^10.0.0",
37
37
  "@inquirer/prompts": "^7.4.0"
38
38
  },
@@ -19,13 +19,21 @@ describe("create-kumiko-app CLI", () => {
19
19
  afterEach(() => rmSync(tmp, { recursive: true, force: true }));
20
20
 
21
21
  test("--yes scaffolds the recommended stack", async () => {
22
+ const startedAt = performance.now();
22
23
  const code = await runCreate({
23
24
  name: "demo-app",
24
25
  yes: true,
25
26
  cwd: tmp,
26
27
  log: (line) => logs.push(line),
27
28
  });
29
+ const scaffoldMs = performance.now() - startedAt;
28
30
  expect(code).toBe(0);
31
+ // Onboarding regression gate: the scaffold step is the first thing a new
32
+ // user waits on. Generous ceiling so slow CI runners pass — what we catch
33
+ // is an order-of-magnitude blowup, not seconds.
34
+ expect(scaffoldMs, `scaffold took ${Math.round(scaffoldMs)}ms (ceiling 30s)`).toBeLessThan(
35
+ 30_000,
36
+ );
29
37
 
30
38
  const dest = join(tmp, "demo-app");
31
39
  expect(existsSync(dest)).toBe(true);
@@ -7,6 +7,7 @@
7
7
  // only actually booting the resolved set through validateBoot can.
8
8
 
9
9
  import { describe, expect, test } from "bun:test";
10
+ import { createPersonalAccessTokensFeature } from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens";
10
11
  import {
11
12
  createRegistry,
12
13
  type FeatureDefinition,
@@ -53,6 +54,11 @@ describe("--yes resolved set boots (issue-1174 regression)", () => {
53
54
 
54
55
  test("the resolved --yes feature set boots without the GDPR PII-hook-coverage error", async () => {
55
56
  const instances = await instantiateResolved(resolved.featureNames);
57
+ // sessions (auto-included, session-list/detail screens are recommended)
58
+ // requires auth-foundation, which the dep-resolver already pulls in
59
+ // transitively — it just needs a tokenVerifier provider mounted too.
60
+ // PAT is what publicstatus/money-horse mount for this today.
61
+ instances.push(createPersonalAccessTokensFeature({ scopes: {} }));
56
62
  const composed = composeFeatures(instances, { includeBundled: true });
57
63
  expect(() => validateBoot(composed)).not.toThrow();
58
64
  expect(() => createRegistry(composed)).not.toThrow();
@@ -29,6 +29,12 @@ export const FEATURE_CONSTRUCTORS: Readonly<Record<string, ScaffoldFeatureEntry>
29
29
  exportName: "createUserFeature",
30
30
  callExpression: "createUserFeature()",
31
31
  },
32
+ "auth-foundation": {
33
+ name: "auth-foundation",
34
+ importPath: "@cosmicdrift/kumiko-bundled-features/auth-foundation",
35
+ exportName: "authFoundationFeature",
36
+ callExpression: "authFoundationFeature",
37
+ },
32
38
  sessions: {
33
39
  name: "sessions",
34
40
  importPath: "@cosmicdrift/kumiko-bundled-features/sessions",