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.
package/feature-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "samples/apps/use-all-bundled APP_FEATURES (composeFeatures includeBundled)",
|
|
3
|
-
"featureCount":
|
|
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.
|
|
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.
|
|
34
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
35
|
-
"@cosmicdrift/kumiko-server-runtime": "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();
|