create-kumiko-app 0.4.148 → 0.4.150

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": 56,
3
+ "featureCount": 57,
4
4
  "features": [
5
5
  {
6
6
  "name": "admin-shell",
@@ -471,6 +471,75 @@
471
471
  "recommended": true
472
472
  }
473
473
  },
474
+ {
475
+ "name": "document-ingest-foundation",
476
+ "description": "Shared PDF/Scan/Image → normalized-text ingest primitive. Owns the `documentExtract` entity (fileRefId, storageKey, per-page text + metadata) as an implicit entity-projection, the per-tenant `ocrLanguage`/`maxPagesPerFile` config keys, and a fileRef.created trigger that validates mime/size and requests ingest via `documentIngest.requested`. LiteParse provider wiring lands in follow-up features.",
477
+ "toggleableDefault": null,
478
+ "requires": [
479
+ "config"
480
+ ],
481
+ "optionalRequires": [],
482
+ "configReads": [],
483
+ "exposesApis": [],
484
+ "usesApis": [],
485
+ "extensionsUsed": [],
486
+ "configKeys": [
487
+ {
488
+ "key": "max-pages-per-file",
489
+ "qualifiedName": "document-ingest-foundation:config:max-pages-per-file",
490
+ "type": "number",
491
+ "scope": "tenant",
492
+ "default": 50,
493
+ "encrypted": false,
494
+ "computed": false,
495
+ "options": null,
496
+ "bounds": {
497
+ "min": 1,
498
+ "max": 500
499
+ },
500
+ "pattern": null,
501
+ "writeRoles": [
502
+ "TenantAdmin",
503
+ "SystemAdmin"
504
+ ],
505
+ "readRoles": [
506
+ "TenantAdmin",
507
+ "SystemAdmin",
508
+ "User"
509
+ ]
510
+ },
511
+ {
512
+ "key": "ocr-language",
513
+ "qualifiedName": "document-ingest-foundation:config:ocr-language",
514
+ "type": "text",
515
+ "scope": "tenant",
516
+ "default": "deu+eng",
517
+ "encrypted": false,
518
+ "computed": false,
519
+ "options": null,
520
+ "bounds": null,
521
+ "pattern": {
522
+ "regex": "^[a-z]{3}(\\+[a-z]{3})*$"
523
+ },
524
+ "writeRoles": [
525
+ "TenantAdmin",
526
+ "SystemAdmin"
527
+ ],
528
+ "readRoles": [
529
+ "TenantAdmin",
530
+ "SystemAdmin",
531
+ "User"
532
+ ]
533
+ }
534
+ ],
535
+ "secrets": [],
536
+ "writeHandlers": [],
537
+ "uiHints": {
538
+ "displayLabel": "Document Ingest Foundation",
539
+ "category": "storage",
540
+ "recommended": false
541
+ }
542
+ },
474
543
  {
475
544
  "name": "feature-toggles",
476
545
  "description": "Persists per-feature enabled/disabled state in the `store_global_feature_state` table and exposes a `set` write-handler plus `list`/`registered` query-handlers so operators can flip features at runtime without redeploying. Each API instance keeps an in-memory `GlobalFeatureToggleRuntime` snapshot (initialize it via `createFeatureToggleRuntime`, pass a `() => runtime` accessor to `createFeatureTogglesFeature`) that the dispatcher gate reads on every request; a `toggle-cache-sync` multi-stream projection with `delivery: \"per-instance\"` syncs the snapshot across instances whenever a `toggle-set` event is appended.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kumiko-app",
3
- "version": "0.4.148",
3
+ "version": "0.4.150",
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.164.0",
34
- "@cosmicdrift/kumiko-framework": "0.164.0",
35
- "@cosmicdrift/kumiko-server-runtime": "0.164.0",
33
+ "@cosmicdrift/kumiko-dev-server": "1.0.0",
34
+ "@cosmicdrift/kumiko-framework": "1.0.0",
35
+ "@cosmicdrift/kumiko-server-runtime": "1.0.0",
36
36
  "@inquirer/core": "^10.0.0",
37
37
  "@inquirer/prompts": "^7.4.0"
38
38
  },
@@ -30,7 +30,10 @@ describe("create-kumiko-app CLI", () => {
30
30
  expect(code).toBe(0);
31
31
  // Onboarding regression gate: the scaffold step is the first thing a new
32
32
  // user waits on. Generous ceiling so slow CI runners pass — what we catch
33
- // is an order-of-magnitude blowup, not seconds.
33
+ // is an order-of-magnitude blowup, not seconds. Needs an explicit test
34
+ // timeout above the 30s ceiling: bun's default (5s) would fail a slow-
35
+ // but-fine CI run on its own timeout before this assertion ever gets to
36
+ // fire, making the "ceiling" the opposite of what the comment claims.
34
37
  expect(scaffoldMs, `scaffold took ${Math.round(scaffoldMs)}ms (ceiling 30s)`).toBeLessThan(
35
38
  30_000,
36
39
  );
@@ -67,7 +70,7 @@ describe("create-kumiko-app CLI", () => {
67
70
 
68
71
  // Setup-impact preview lands before the scaffold actually runs.
69
72
  expect(out).toMatch(/→ Scaffolding \d+ features? into \.\/demo-app\//);
70
- });
73
+ }, 35_000);
71
74
 
72
75
  test("--print-manifest emits JSON, no name needed", async () => {
73
76
  const code = await runCreate({
@@ -0,0 +1,48 @@
1
+ // Guards against #1425/1: callExpression (rendered into generated
2
+ // run-config.ts) and callArgs (used at runtime by instantiateScaffoldFeatures)
3
+ // encode the same call twice by hand. If someone edits one without the
4
+ // other, the boot test validates a different feature config than what
5
+ // actually gets scaffolded. This derives the expected callExpression from
6
+ // exportName + callArgs for every entry and diffs it against the literal.
7
+
8
+ import { describe, expect, test } from "bun:test";
9
+ import { FEATURE_CONSTRUCTORS } from "../feature-constructors";
10
+
11
+ function expectedCallExpression(
12
+ exportName: string,
13
+ callArgs: readonly unknown[] | undefined,
14
+ ): string {
15
+ if (callArgs === undefined) {
16
+ // Ambiguous by callArgs alone: could be a zero-arg factory (`fn()`) or a
17
+ // bare object export (`fn`). Disambiguate the same way the runtime does
18
+ // (scaffold-app.ts's instantiateScaffoldFeatures): trust the "()" the
19
+ // literal itself carries, then only check the exportName prefix matches.
20
+ return exportName;
21
+ }
22
+ const args = callArgs.map((a) => JSON.stringify(a)).join(", ");
23
+ return `${exportName}(${args})`;
24
+ }
25
+
26
+ describe("FEATURE_CONSTRUCTORS callExpression/callArgs consistency", () => {
27
+ for (const [name, entry] of Object.entries(FEATURE_CONSTRUCTORS)) {
28
+ test(`${name}: callExpression matches exportName + callArgs`, () => {
29
+ if (entry.callArgs !== undefined) {
30
+ // callExpression is hand-formatted TS source (`{ scopes: {} }`), not
31
+ // JSON (`{"scopes":{}}`) — compare with whitespace stripped so both
32
+ // are checked against the same underlying arg values.
33
+ const normalize = (s: string) => s.replace(/[\s"]+/g, "");
34
+ expect(normalize(entry.callExpression)).toBe(
35
+ normalize(expectedCallExpression(entry.exportName, entry.callArgs)),
36
+ );
37
+ return;
38
+ }
39
+ // No callArgs: either `exportName()` (zero-arg factory) or bare
40
+ // `exportName` (object export) — both are legal, but the export name
41
+ // itself must be the prefix either way.
42
+ expect(
43
+ entry.callExpression === entry.exportName ||
44
+ entry.callExpression === `${entry.exportName}()`,
45
+ ).toBe(true);
46
+ });
47
+ }
48
+ });
@@ -52,11 +52,12 @@ describe("--yes resolved set boots (issue-1174 regression)", () => {
52
52
  });
53
53
 
54
54
  test("the resolved --yes feature set boots without the GDPR PII-hook-coverage error", async () => {
55
- const instances = await instantiateResolved(resolved.featureNames);
56
55
  // sessions (auto-included, session-list/detail screens are recommended)
57
56
  // requires auth-foundation, which the dep-resolver already pulls in
58
57
  // transitively — personal-access-tokens (recommended, satisfies the
59
58
  // tokenVerifier boot invariant) is resolved into the set too.
59
+ expect(resolved.featureNames).toContain("personal-access-tokens");
60
+ const instances = await instantiateResolved(resolved.featureNames);
60
61
  const composed = composeFeatures(instances, { includeBundled: true });
61
62
  expect(() => validateBoot(composed)).not.toThrow();
62
63
  expect(() => createRegistry(composed)).not.toThrow();