okengine 0.3.4 → 0.3.6
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/AGENTS.md +4 -0
- package/manifest.v1.schema.json +43 -1
- package/package.json +16 -2
- package/site/content/docs/ai/skills.mdx +9 -7
- package/site/content/docs/console/vault.mdx +4 -0
- package/site/content/docs/elements/channel.mdx +5 -4
- package/site/content/docs/elements/clock.mdx +1 -0
- package/site/content/docs/elements/flow.mdx +15 -0
- package/site/content/docs/elements/gate.mdx +16 -11
- package/site/content/docs/elements/signal.mdx +8 -7
- package/site/content/docs/elements/store.mdx +48 -8
- package/site/content/docs/elements/vault.mdx +1 -1
- package/site/content/docs/plugins/ip-allowlist.mdx +2 -2
- package/site/content/docs/reference/configuration.mdx +2 -2
- package/site/content/docs/reference/environment-variables.mdx +8 -0
- package/site/content/docs/reference/fx.mdx +40 -0
- package/site/content/docs/reference/plugins.mdx +18 -18
- package/src/compiler/extract.test.ts +63 -0
- package/src/compiler/extract.ts +36 -15
- package/src/console/server/channels.ts +2 -0
- package/src/console/server/clock.ts +3 -0
- package/src/console/server/flows.ts +13 -0
- package/src/console/server/gates.ts +2 -0
- package/src/console/server/plugins.ts +20 -1
- package/src/console/server/signals.ts +5 -0
- package/src/console/server/store.test.ts +17 -0
- package/src/console/server/store.ts +24 -0
- package/src/console/ui/channels/types.ts +1 -0
- package/src/console/ui/clock/types.ts +1 -0
- package/src/console/ui/display.test.ts +14 -0
- package/src/console/ui/display.ts +9 -0
- package/src/console/ui/dist/assets/{index-BWo8R7NR.js → index-CrKMmO__.js} +2 -2
- package/src/console/ui/dist/assets/panel-channels-DCDd4WAC.js +1 -0
- package/src/console/ui/dist/assets/panel-clock-DjGGFPzr.js +1 -0
- package/src/console/ui/dist/assets/panel-gates-B5eTE8XH.js +1 -0
- package/src/console/ui/dist/assets/{panel-overview-BznEOTnb.js → panel-overview-BsFvDdts.js} +1 -1
- package/src/console/ui/dist/assets/panel-plugins-Cj7DK1er.js +1 -0
- package/src/console/ui/dist/assets/{panel-runs-CGWNHLR4.js → panel-runs-C0gmnoYL.js} +1 -1
- package/src/console/ui/dist/assets/panel-signals-whmDXIg3.js +1 -0
- package/src/console/ui/dist/assets/panel-store-CEMHLvaw.js +1 -0
- package/src/console/ui/dist/assets/{panel-traces-DBLx2ilD.js → panel-traces-BDiAuVSK.js} +1 -1
- package/src/console/ui/dist/assets/panel-vault-C9wjbki8.js +1 -0
- package/src/console/ui/dist/index.html +1 -1
- package/src/console/ui/gates/types.ts +1 -0
- package/src/console/ui/plugins/fixture.ts +7 -0
- package/src/console/ui/plugins/types.ts +3 -0
- package/src/console/ui/shell/client.ts +3 -0
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +7 -2
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +9 -2
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +12 -5
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +18 -4
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +13 -2
- package/src/console/ui/shell/panels/store/StorePanel.tsx +11 -4
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +9 -3
- package/src/console/ui/signals/types.ts +1 -0
- package/src/console/ui/store/fixture.ts +5 -0
- package/src/console/ui/store/types.ts +2 -0
- package/src/drivers/conformance.test.ts +11 -0
- package/src/drivers/drizzle-dialect.test.ts +4 -0
- package/src/drivers/drizzle-dialect.ts +8 -4
- package/src/drivers/index.ts +4 -0
- package/src/drivers/libsql.ts +179 -0
- package/src/drivers/pglite.ts +79 -0
- package/src/drivers/pgvector.ts +54 -19
- package/src/drivers/types.ts +16 -7
- package/src/elements/channel/declare.ts +5 -0
- package/src/elements/clock/declare.ts +5 -0
- package/src/elements/clock/durable.ts +7 -1
- package/src/elements/gate/declare.ts +28 -5
- package/src/elements/gate.ts +1 -0
- package/src/elements/signal/declare.ts +5 -0
- package/src/elements/store/declare.ts +10 -2
- package/src/elements/store/index-boot.test.ts +257 -0
- package/src/elements/store/runtime.ts +67 -9
- package/src/elements/store/schema-decl.ts +7 -0
- package/src/kernel/abort-scope.ts +116 -0
- package/src/kernel/app.ts +12 -2
- package/src/kernel/boot-bind/store.test.ts +59 -1
- package/src/kernel/boot-bind/store.ts +64 -2
- package/src/kernel/concurrency.test.ts +236 -0
- package/src/kernel/concurrency.ts +172 -0
- package/src/kernel/flow.ts +9 -0
- package/src/kernel/fx.test.ts +12 -0
- package/src/kernel/fx.ts +44 -0
- package/src/kernel/index.ts +14 -0
- package/src/kernel/journal.ts +9 -0
- package/src/kernel/plugin/capabilities.test.ts +18 -0
- package/src/kernel/plugin.ts +11 -3
- package/src/kernel/registry.ts +29 -6
- package/src/manifest/types.ts +21 -0
- package/src/release/measure.ts +4 -0
- package/src/console/ui/dist/assets/panel-channels-BOmQ-onL.js +0 -1
- package/src/console/ui/dist/assets/panel-clock-giAq0Ccv.js +0 -1
- package/src/console/ui/dist/assets/panel-gates-XclZxWD5.js +0 -1
- package/src/console/ui/dist/assets/panel-plugins-CcGM1g64.js +0 -1
- package/src/console/ui/dist/assets/panel-signals-CNywkdak.js +0 -1
- package/src/console/ui/dist/assets/panel-store-KmTbFHMH.js +0 -1
- package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +0 -1
|
@@ -240,6 +240,69 @@ export const db = store.sql("app", { schema: { notes } });
|
|
|
240
240
|
sqlName: "id",
|
|
241
241
|
});
|
|
242
242
|
});
|
|
243
|
+
|
|
244
|
+
test("extracts optional description fields additively", async () => {
|
|
245
|
+
const source = `
|
|
246
|
+
import { store, field, signal, channel, clock, gate, vault } from "okengine";
|
|
247
|
+
|
|
248
|
+
export const notes = store.schema.table("notes", {
|
|
249
|
+
title: field.text().notNull().describe("Note title"),
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
export const db = store.sql("app", {
|
|
253
|
+
description: "Primary app database",
|
|
254
|
+
schema: { notes },
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
export const embeddings = store.index("embeddings", {
|
|
258
|
+
description: "Document embeddings",
|
|
259
|
+
dims: 3,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
export const sessions = store.kv("sessions", { description: "Session cache" });
|
|
263
|
+
|
|
264
|
+
export const orderPlaced = signal("order-placed", {
|
|
265
|
+
delivery: "once",
|
|
266
|
+
description: "Order placed event",
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
export const bookingConfirmed = channel.template("booking-confirmed", {
|
|
270
|
+
medium: "email",
|
|
271
|
+
description: "Booking confirmation email",
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
export const expireHolds = clock("expire-holds", {
|
|
275
|
+
every: "10m",
|
|
276
|
+
description: "Expire unpaid holds",
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
export const member = gate.policy("member", {
|
|
280
|
+
description: "Verified members only",
|
|
281
|
+
check: ({ auth }) => !!auth?.verified,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
export const stripeKey = vault.secret("STRIPE_KEY", {
|
|
285
|
+
description: "Payments gateway key",
|
|
286
|
+
});
|
|
287
|
+
`;
|
|
288
|
+
const manifest = await extractFromSources({
|
|
289
|
+
"src/described.ts": source,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
expect(manifest.stores?.app?.description).toBe("Primary app database");
|
|
293
|
+
expect(manifest.stores?.app?.tables?.notes?.columns?.title).toMatchObject({
|
|
294
|
+
description: "Note title",
|
|
295
|
+
});
|
|
296
|
+
expect(manifest.stores?.embeddings?.description).toBe("Document embeddings");
|
|
297
|
+
expect(manifest.stores?.sessions?.description).toBe("Session cache");
|
|
298
|
+
expect(manifest.signals?.["order-placed"]?.description).toBe("Order placed event");
|
|
299
|
+
expect(manifest.channels?.["booking-confirmed"]?.description).toBe(
|
|
300
|
+
"Booking confirmation email",
|
|
301
|
+
);
|
|
302
|
+
expect(manifest.clocks?.["expire-holds"]?.description).toBe("Expire unpaid holds");
|
|
303
|
+
expect(manifest.gates?.member?.description).toBe("Verified members only");
|
|
304
|
+
expect(manifest.vault?.STRIPE_KEY?.description).toBe("Payments gateway key");
|
|
305
|
+
});
|
|
243
306
|
});
|
|
244
307
|
|
|
245
308
|
describe("extractManifest — on(http.resource(...))", () => {
|
package/src/compiler/extract.ts
CHANGED
|
@@ -420,6 +420,7 @@ function parseFieldChain(node: AstNode, key: string): DeclaredColumn | undefined
|
|
|
420
420
|
const chain: string[] = [];
|
|
421
421
|
let sqlType: "text" | "integer" | undefined;
|
|
422
422
|
let sqlName: string | undefined;
|
|
423
|
+
let description: string | undefined;
|
|
423
424
|
let defaultValue: string | number | boolean | null | undefined;
|
|
424
425
|
let hasDefault = false;
|
|
425
426
|
let cur: AstNode | undefined = node;
|
|
@@ -435,6 +436,9 @@ function parseFieldChain(node: AstNode, key: string): DeclaredColumn | undefined
|
|
|
435
436
|
if (method === "as") {
|
|
436
437
|
sqlName = stringArg(call.arguments[0]) ?? sqlName;
|
|
437
438
|
}
|
|
439
|
+
if (method === "describe") {
|
|
440
|
+
description = stringArg(call.arguments[0]) ?? description;
|
|
441
|
+
}
|
|
438
442
|
if (method === "default") {
|
|
439
443
|
const lit = call.arguments[0];
|
|
440
444
|
if (lit && lit.type === "Literal") {
|
|
@@ -504,6 +508,7 @@ function parseFieldChain(node: AstNode, key: string): DeclaredColumn | undefined
|
|
|
504
508
|
...(methods.has("primaryKey") ? { primaryKey: true } : {}),
|
|
505
509
|
...(methods.has("unique") ? { unique: true } : {}),
|
|
506
510
|
...(hasDefault ? { default: defaultValue ?? null } : {}),
|
|
511
|
+
...(description !== undefined ? { description } : {}),
|
|
507
512
|
...(methods.has("pii") ? { pii: true } : {}),
|
|
508
513
|
...(methods.has("sensitive") ? { sensitive: true } : {}),
|
|
509
514
|
};
|
|
@@ -597,7 +602,12 @@ function visitDeclarationCall(call: CallExpression, program: AstNode, scope: Pro
|
|
|
597
602
|
const storeName = stringArg(call.arguments[0]) ?? "store";
|
|
598
603
|
const ref = `${facet}:${storeName}` as const;
|
|
599
604
|
const bindingName = enclosingConstName(call, program);
|
|
605
|
+
const storeOpts = objectArg(call.arguments[1]);
|
|
606
|
+
const description = stringProp(storeOpts, "description");
|
|
600
607
|
scope.stores[storeName] = scope.stores[storeName] ?? { facet };
|
|
608
|
+
if (description) {
|
|
609
|
+
scope.stores[storeName]!.description = description;
|
|
610
|
+
}
|
|
601
611
|
if (facet === "sql") {
|
|
602
612
|
attachSchemaOption(call.arguments[1], scope.stores[storeName]!, scope);
|
|
603
613
|
}
|
|
@@ -643,7 +653,13 @@ function visitDeclarationCall(call: CallExpression, program: AstNode, scope: Pro
|
|
|
643
653
|
if (obj === "gate" && prop === "policy") {
|
|
644
654
|
const policyName = stringArg(call.arguments[0]);
|
|
645
655
|
if (policyName) {
|
|
646
|
-
|
|
656
|
+
const opts = objectArg(call.arguments[1]);
|
|
657
|
+
const description = stringProp(opts, "description");
|
|
658
|
+
scope.gates[policyName] = {
|
|
659
|
+
kind: "policy",
|
|
660
|
+
roles: [policyName],
|
|
661
|
+
...(description ? { description } : {}),
|
|
662
|
+
};
|
|
647
663
|
const bindingName = enclosingConstName(call, program);
|
|
648
664
|
if (bindingName) {
|
|
649
665
|
scope.gateIds.set(bindingName, policyName);
|
|
@@ -661,26 +677,23 @@ function visitDeclarationCall(call: CallExpression, program: AstNode, scope: Pro
|
|
|
661
677
|
const max = numberProp(opts, "max");
|
|
662
678
|
const per = stringProp(opts, "per");
|
|
663
679
|
const keyBy = stringProp(opts, "keyBy");
|
|
680
|
+
const description = stringProp(opts, "description");
|
|
664
681
|
if (max !== undefined && per) {
|
|
665
682
|
const expr = `rate:${strategy}:${max}/${per}`;
|
|
666
683
|
const bindingName = enclosingConstName(call, program);
|
|
684
|
+
const rateGate = {
|
|
685
|
+
kind: "rate" as const,
|
|
686
|
+
strategy,
|
|
687
|
+
max,
|
|
688
|
+
per,
|
|
689
|
+
...(keyBy ? { keyBy } : {}),
|
|
690
|
+
...(description ? { description } : {}),
|
|
691
|
+
};
|
|
667
692
|
if (bindingName) {
|
|
668
|
-
scope.gates[bindingName] =
|
|
669
|
-
kind: "rate",
|
|
670
|
-
strategy,
|
|
671
|
-
max,
|
|
672
|
-
per,
|
|
673
|
-
...(keyBy ? { keyBy } : {}),
|
|
674
|
-
};
|
|
693
|
+
scope.gates[bindingName] = rateGate;
|
|
675
694
|
scope.gateIds.set(bindingName, expr);
|
|
676
695
|
} else {
|
|
677
|
-
scope.gates[expr] =
|
|
678
|
-
kind: "rate",
|
|
679
|
-
strategy,
|
|
680
|
-
max,
|
|
681
|
-
per,
|
|
682
|
-
...(keyBy ? { keyBy } : {}),
|
|
683
|
-
};
|
|
696
|
+
scope.gates[expr] = rateGate;
|
|
684
697
|
}
|
|
685
698
|
}
|
|
686
699
|
}
|
|
@@ -691,9 +704,11 @@ function visitDeclarationCall(call: CallExpression, program: AstNode, scope: Pro
|
|
|
691
704
|
if (templateName) {
|
|
692
705
|
const medium = stringProp(opts, "medium");
|
|
693
706
|
const locales = stringArrayProp(opts, "locales");
|
|
707
|
+
const description = stringProp(opts, "description");
|
|
694
708
|
scope.channels[templateName] = {
|
|
695
709
|
...(medium ? { medium: medium as Channel["medium"] } : { medium: "email" }),
|
|
696
710
|
...(locales ? { locales } : {}),
|
|
711
|
+
...(description ? { description } : {}),
|
|
697
712
|
};
|
|
698
713
|
const bindingName = enclosingConstName(call, program);
|
|
699
714
|
if (bindingName) {
|
|
@@ -809,6 +824,9 @@ function visitDeclarationCall(call: CallExpression, program: AstNode, scope: Pro
|
|
|
809
824
|
if (delivery) {
|
|
810
825
|
const signal: Signal = {
|
|
811
826
|
delivery,
|
|
827
|
+
...(stringProp(opts, "description")
|
|
828
|
+
? { description: stringProp(opts, "description") }
|
|
829
|
+
: {}),
|
|
812
830
|
...(numberProp(opts, "retries") !== undefined
|
|
813
831
|
? { retries: numberProp(opts, "retries") }
|
|
814
832
|
: {}),
|
|
@@ -839,6 +857,9 @@ function visitDeclarationCall(call: CallExpression, program: AstNode, scope: Pro
|
|
|
839
857
|
...(boolProp(opts, "overridable") !== undefined
|
|
840
858
|
? { overridable: boolProp(opts, "overridable") }
|
|
841
859
|
: {}),
|
|
860
|
+
...(stringProp(opts, "description")
|
|
861
|
+
? { description: stringProp(opts, "description") }
|
|
862
|
+
: {}),
|
|
842
863
|
};
|
|
843
864
|
}
|
|
844
865
|
}
|
|
@@ -41,6 +41,7 @@ export type ChannelsFace = "inbox" | "deliverability";
|
|
|
41
41
|
/** One Manifest template row. */
|
|
42
42
|
export interface ConsoleChannelTemplate {
|
|
43
43
|
readonly name: string;
|
|
44
|
+
readonly description?: string;
|
|
44
45
|
readonly medium: string;
|
|
45
46
|
readonly locales: readonly string[];
|
|
46
47
|
readonly from: string | null;
|
|
@@ -214,6 +215,7 @@ export function projectTemplates(manifest: Manifest | null): readonly ConsoleCha
|
|
|
214
215
|
return Object.entries(manifest?.channels ?? {})
|
|
215
216
|
.map(([name, c]) => ({
|
|
216
217
|
name,
|
|
218
|
+
...(c.description !== undefined ? { description: c.description } : {}),
|
|
217
219
|
medium: c.medium ?? "email",
|
|
218
220
|
locales: c.locales ?? [],
|
|
219
221
|
from: c.from ?? null,
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
/** One cron row in `console.clock.list`. */
|
|
29
29
|
export interface ConsoleClockRow {
|
|
30
30
|
readonly name: string;
|
|
31
|
+
readonly description?: string;
|
|
31
32
|
readonly status: CronRow["status"];
|
|
32
33
|
readonly timezone: string;
|
|
33
34
|
readonly overridable: boolean;
|
|
@@ -110,8 +111,10 @@ export async function projectClocksList(options: ProjectClocksOptions): Promise<
|
|
|
110
111
|
.map((row) => {
|
|
111
112
|
const flowIds = flowIdsForCronRow(row, options.manifest);
|
|
112
113
|
const external = flowIds.some((id) => isExternal(flows[id]));
|
|
114
|
+
const description = options.manifest?.clocks?.[row.name]?.description;
|
|
113
115
|
return {
|
|
114
116
|
name: row.name,
|
|
117
|
+
...(description !== undefined ? { description } : {}),
|
|
115
118
|
status: row.status,
|
|
116
119
|
timezone: row.timezone,
|
|
117
120
|
overridable: row.overridable,
|
|
@@ -158,6 +158,7 @@ const SignalsListOut = z.object({
|
|
|
158
158
|
signals: z.array(
|
|
159
159
|
z.object({
|
|
160
160
|
name: z.string(),
|
|
161
|
+
description: z.string().optional(),
|
|
161
162
|
delivery: z.enum(["once", "broadcast", "live"]),
|
|
162
163
|
retries: z.number(),
|
|
163
164
|
deadLetterEnabled: z.boolean(),
|
|
@@ -336,6 +337,7 @@ const ChannelsListOut = z.object({
|
|
|
336
337
|
templates: z.array(
|
|
337
338
|
z.object({
|
|
338
339
|
name: z.string(),
|
|
340
|
+
description: z.string().optional(),
|
|
339
341
|
medium: z.string(),
|
|
340
342
|
locales: z.array(z.string()),
|
|
341
343
|
from: z.string().nullable(),
|
|
@@ -465,6 +467,7 @@ const CronHealthOut = z.object({
|
|
|
465
467
|
|
|
466
468
|
const ClockCronOut = z.object({
|
|
467
469
|
name: z.string(),
|
|
470
|
+
description: z.string().optional(),
|
|
468
471
|
status: z.enum(["active", "paused", "orphaned"]),
|
|
469
472
|
timezone: z.string(),
|
|
470
473
|
overridable: z.boolean(),
|
|
@@ -753,6 +756,7 @@ const GatesListOut = z.object({
|
|
|
753
756
|
gates: z.array(
|
|
754
757
|
z.object({
|
|
755
758
|
name: z.string(),
|
|
759
|
+
description: z.string().optional(),
|
|
756
760
|
kind: z.enum(["policy", "rate"]),
|
|
757
761
|
scopes: z.array(z.string()),
|
|
758
762
|
roles: z.array(z.string()),
|
|
@@ -853,6 +857,13 @@ const PluginsListOut = z.object({
|
|
|
853
857
|
}),
|
|
854
858
|
),
|
|
855
859
|
declares: z.array(z.string()),
|
|
860
|
+
tables: z.record(
|
|
861
|
+
z.string(),
|
|
862
|
+
z.object({
|
|
863
|
+
plane: z.string().optional(),
|
|
864
|
+
description: z.string().optional(),
|
|
865
|
+
}),
|
|
866
|
+
),
|
|
856
867
|
intercepts: z.array(
|
|
857
868
|
z.object({
|
|
858
869
|
stage: z.string(),
|
|
@@ -1216,6 +1227,7 @@ const StoreListOut = z.object({
|
|
|
1216
1227
|
ref: z.string(),
|
|
1217
1228
|
facet: z.enum(["sql", "kv", "files", "index"]),
|
|
1218
1229
|
name: z.string(),
|
|
1230
|
+
description: z.string().optional(),
|
|
1219
1231
|
children: z.array(
|
|
1220
1232
|
z.object({
|
|
1221
1233
|
name: z.string(),
|
|
@@ -1229,6 +1241,7 @@ const StoreListOut = z.object({
|
|
|
1229
1241
|
}),
|
|
1230
1242
|
willNotFire: WillNotFireOut,
|
|
1231
1243
|
piiColumns: z.array(z.string()),
|
|
1244
|
+
columnDescriptions: z.record(z.string(), z.string()),
|
|
1232
1245
|
}),
|
|
1233
1246
|
),
|
|
1234
1247
|
replicaLagMs: z.number().nullable(),
|
|
@@ -48,6 +48,7 @@ export type GatePrincipalKind = "role" | "key" | "user";
|
|
|
48
48
|
/** One Module:Action / flow / gate surface row. */
|
|
49
49
|
export interface ConsoleGateDefRow {
|
|
50
50
|
readonly name: string;
|
|
51
|
+
readonly description?: string;
|
|
51
52
|
readonly kind: "policy" | "rate";
|
|
52
53
|
readonly scopes: readonly string[];
|
|
53
54
|
readonly roles: readonly string[];
|
|
@@ -477,6 +478,7 @@ function projectGateDefs(
|
|
|
477
478
|
def.kind === "rate" || name.startsWith("rate:") ? "rate" : "policy";
|
|
478
479
|
rows.push({
|
|
479
480
|
name,
|
|
481
|
+
...(def.description !== undefined ? { description: def.description } : {}),
|
|
480
482
|
kind,
|
|
481
483
|
scopes: [...(def.scopes ?? [])],
|
|
482
484
|
roles: [...(def.roles ?? [])],
|
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
import { allHookCostSummaries, type HookCostSummary } from "../../kernel/hook-timing.ts";
|
|
10
10
|
import type { PluginRegistry } from "../../kernel/registry.ts";
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
Manifest,
|
|
13
|
+
ManifestChange,
|
|
14
|
+
Plugin,
|
|
15
|
+
PluginOrigin,
|
|
16
|
+
PluginTable,
|
|
17
|
+
} from "../../manifest/types.ts";
|
|
12
18
|
import { CORE_PLUGINS, isCorePluginOn, type PluginConfigProbe } from "../../plugins/catalogue.ts";
|
|
13
19
|
import type { PackageJsonProbe, SupplyChainSignals } from "../../plugins/supply-chain.ts";
|
|
14
20
|
import type { ScanSourceFile } from "../../plugins/node-import-scan.ts";
|
|
@@ -46,6 +52,8 @@ export interface ConsolePluginRow {
|
|
|
46
52
|
readonly summary: string | null;
|
|
47
53
|
readonly scopes: readonly PluginScopeView[];
|
|
48
54
|
readonly declares: readonly string[];
|
|
55
|
+
/** Optional metadata for `table:*` declares. */
|
|
56
|
+
readonly tables: Readonly<Record<string, PluginTable>>;
|
|
49
57
|
readonly intercepts: readonly PluginInterceptView[];
|
|
50
58
|
readonly hookCost: {
|
|
51
59
|
readonly count: number;
|
|
@@ -133,6 +141,7 @@ export async function projectPluginsList(
|
|
|
133
141
|
version: meta?.version ?? manifestPlugin?.version ?? null,
|
|
134
142
|
summary: spec.summary,
|
|
135
143
|
declares: meta?.declares ?? manifestPlugin?.declares ?? [],
|
|
144
|
+
tables: meta?.tables ?? manifestPlugin?.tables ?? {},
|
|
136
145
|
interceptStages: meta?.intercepts ?? manifestPlugin?.intercepts ?? [],
|
|
137
146
|
scopes: meta?.scopes ?? [],
|
|
138
147
|
costs,
|
|
@@ -155,6 +164,7 @@ export async function projectPluginsList(
|
|
|
155
164
|
version: meta.version,
|
|
156
165
|
summary: null,
|
|
157
166
|
declares: meta.declares,
|
|
167
|
+
tables: meta.tables,
|
|
158
168
|
interceptStages: meta.intercepts,
|
|
159
169
|
scopes: meta.scopes,
|
|
160
170
|
costs,
|
|
@@ -184,6 +194,7 @@ interface PluggedMeta {
|
|
|
184
194
|
readonly version: string | null;
|
|
185
195
|
readonly declares: readonly string[];
|
|
186
196
|
readonly intercepts: readonly string[];
|
|
197
|
+
readonly tables: Readonly<Record<string, PluginTable>>;
|
|
187
198
|
readonly scopes: readonly PluginScopeView[];
|
|
188
199
|
readonly origin?: PluginOrigin;
|
|
189
200
|
}
|
|
@@ -199,6 +210,7 @@ function collectPlugged(
|
|
|
199
210
|
version: p.version ?? null,
|
|
200
211
|
declares: p.declares ?? [],
|
|
201
212
|
intercepts: p.intercepts ?? [],
|
|
213
|
+
tables: p.tables ?? {},
|
|
202
214
|
scopes: [],
|
|
203
215
|
origin: p.origin,
|
|
204
216
|
});
|
|
@@ -214,10 +226,15 @@ function collectPlugged(
|
|
|
214
226
|
? { kind: "app" }
|
|
215
227
|
: { kind: entry.scope.kind, name: entry.scope.name };
|
|
216
228
|
const scopes = [...(prev?.scopes ?? []), scope];
|
|
229
|
+
const tables = {
|
|
230
|
+
...(prev?.tables ?? {}),
|
|
231
|
+
...(caps.tables ?? {}),
|
|
232
|
+
};
|
|
217
233
|
out.set(id, {
|
|
218
234
|
version: caps.version ?? prev?.version ?? null,
|
|
219
235
|
declares: caps.declares.length ? [...caps.declares] : (prev?.declares ?? []),
|
|
220
236
|
intercepts: caps.intercepts.length ? [...caps.intercepts] : (prev?.intercepts ?? []),
|
|
237
|
+
tables,
|
|
221
238
|
scopes,
|
|
222
239
|
origin: prev?.origin,
|
|
223
240
|
});
|
|
@@ -243,6 +260,7 @@ async function buildRow(input: {
|
|
|
243
260
|
readonly version: string | null;
|
|
244
261
|
readonly summary: string | null;
|
|
245
262
|
readonly declares: readonly string[];
|
|
263
|
+
readonly tables: Readonly<Record<string, PluginTable>>;
|
|
246
264
|
readonly interceptStages: readonly string[];
|
|
247
265
|
readonly scopes: readonly PluginScopeView[];
|
|
248
266
|
readonly costs: Readonly<Record<string, HookCostSummary>>;
|
|
@@ -286,6 +304,7 @@ async function buildRow(input: {
|
|
|
286
304
|
summary: input.summary,
|
|
287
305
|
scopes: input.scopes,
|
|
288
306
|
declares: [...input.declares],
|
|
307
|
+
tables: { ...input.tables },
|
|
289
308
|
intercepts,
|
|
290
309
|
hookCost: cost
|
|
291
310
|
? {
|
|
@@ -28,6 +28,7 @@ export interface SignalEndpoint {
|
|
|
28
28
|
/** One row in `console.signals.list`. */
|
|
29
29
|
export interface ConsoleSignalRow {
|
|
30
30
|
readonly name: string;
|
|
31
|
+
readonly description?: string;
|
|
31
32
|
readonly delivery: "once" | "broadcast" | "live";
|
|
32
33
|
readonly retries: number;
|
|
33
34
|
readonly deadLetterEnabled: boolean;
|
|
@@ -112,8 +113,10 @@ export async function projectSignalsList(
|
|
|
112
113
|
withCause(dl, cfg.name, options.runs),
|
|
113
114
|
);
|
|
114
115
|
|
|
116
|
+
const description = options.manifest?.signals?.[cfg.name]?.description;
|
|
115
117
|
rows.push({
|
|
116
118
|
name: cfg.name,
|
|
119
|
+
...(description !== undefined ? { description } : {}),
|
|
117
120
|
delivery: cfg.delivery,
|
|
118
121
|
retries: stats?.retries ?? cfg.retries,
|
|
119
122
|
deadLetterEnabled: stats?.deadLetterEnabled ?? cfg.deadLetter,
|
|
@@ -142,8 +145,10 @@ export async function projectSignalsList(
|
|
|
142
145
|
if (rows.some((r) => r.name === name)) continue;
|
|
143
146
|
const producers = producersOf(name, flows);
|
|
144
147
|
const consumers = consumersOf(name, flows);
|
|
148
|
+
const description = options.manifest?.signals?.[name]?.description;
|
|
145
149
|
rows.push({
|
|
146
150
|
name,
|
|
151
|
+
...(description !== undefined ? { description } : {}),
|
|
147
152
|
delivery: stats.delivery,
|
|
148
153
|
retries: stats.retries,
|
|
149
154
|
deadLetterEnabled: stats.deadLetterEnabled,
|
|
@@ -130,6 +130,23 @@ describe("willNotFireFor", () => {
|
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
+
describe("index driver resolution", () => {
|
|
134
|
+
test("console manifest runtime uses the shared boot switch (memory default)", async () => {
|
|
135
|
+
const runtime = await createManifestStoreRuntime(MANIFEST);
|
|
136
|
+
const decl = runtime.declarations.get("index:docs");
|
|
137
|
+
expect(decl).toBeDefined();
|
|
138
|
+
const handle = (await runtime.open(decl!, {
|
|
139
|
+
effects: { reads: ["index:docs"], writes: ["index:docs"] },
|
|
140
|
+
})) as import("../../elements/store.ts").IndexStoreFxHandle;
|
|
141
|
+
expect(handle.driverId).toBe("memory");
|
|
142
|
+
await handle.upsert("d1", [1, 0, 0], { t: 1 });
|
|
143
|
+
const hits = await handle.search([1, 0, 0], 1);
|
|
144
|
+
expect(hits[0]?.id).toBe("d1");
|
|
145
|
+
expect(hits[0]?.meta).toEqual({ t: 1 });
|
|
146
|
+
await runtime.close();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
133
150
|
describe("PII masking survives SELECT *", () => {
|
|
134
151
|
test("masks classified columns; reveal returns cleartext", async () => {
|
|
135
152
|
const runtime = await createManifestStoreRuntime(MANIFEST);
|
|
@@ -69,6 +69,8 @@ export interface ConsoleStoreChild {
|
|
|
69
69
|
readonly cache: ConsoleStoreCacheView;
|
|
70
70
|
readonly willNotFire: ConsoleWillNotFire;
|
|
71
71
|
readonly piiColumns: readonly string[];
|
|
72
|
+
/** Column key → optional human description (SQL tables). */
|
|
73
|
+
readonly columnDescriptions: Readonly<Record<string, string>>;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
/** One row in `console.store.list`. */
|
|
@@ -76,6 +78,7 @@ export interface ConsoleStoreRow {
|
|
|
76
78
|
readonly ref: ResourceRef;
|
|
77
79
|
readonly facet: StoreFacet;
|
|
78
80
|
readonly name: string;
|
|
81
|
+
readonly description?: string;
|
|
79
82
|
readonly children: readonly ConsoleStoreChild[];
|
|
80
83
|
readonly replicaLagMs: number | null;
|
|
81
84
|
readonly migrationDrift: ConsoleMigrationDrift | null;
|
|
@@ -152,6 +155,7 @@ export async function projectStoresList(options: ProjectStoresOptions): Promise<
|
|
|
152
155
|
ref,
|
|
153
156
|
facet,
|
|
154
157
|
name,
|
|
158
|
+
...(store.description !== undefined ? { description: store.description } : {}),
|
|
155
159
|
children,
|
|
156
160
|
replicaLagMs,
|
|
157
161
|
migrationDrift: facet === "sql" ? drift : null,
|
|
@@ -219,10 +223,27 @@ function childrenOf(
|
|
|
219
223
|
},
|
|
220
224
|
willNotFire,
|
|
221
225
|
piiColumns,
|
|
226
|
+
columnDescriptions: columnDescriptionsFor(store, childName),
|
|
222
227
|
};
|
|
223
228
|
});
|
|
224
229
|
}
|
|
225
230
|
|
|
231
|
+
function columnDescriptionsFor(
|
|
232
|
+
store: NonNullable<Manifest["stores"]>[string],
|
|
233
|
+
tableName: string,
|
|
234
|
+
): Readonly<Record<string, string>> {
|
|
235
|
+
const cols = store.tables?.[tableName]?.columns;
|
|
236
|
+
if (!cols) return {};
|
|
237
|
+
const out: Record<string, string> = {};
|
|
238
|
+
for (const [key, col] of Object.entries(cols)) {
|
|
239
|
+
if (col && typeof col === "object" && "description" in col) {
|
|
240
|
+
const d = (col as { description?: string }).description;
|
|
241
|
+
if (typeof d === "string" && d.length > 0) out[key] = d;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
|
|
226
247
|
function flowsTouching(manifest: Manifest, ref: ResourceRef, kind: "reads" | "writes"): string[] {
|
|
227
248
|
const out: string[] = [];
|
|
228
249
|
for (const [flowId, flow] of Object.entries(manifest.flows ?? {})) {
|
|
@@ -779,6 +800,9 @@ export function cacheKeyInvalidatedBy(key: string, writeRef: ResourceRef): boole
|
|
|
779
800
|
/**
|
|
780
801
|
* Open a memory StoreRuntime seeded from Manifest stores (Console default).
|
|
781
802
|
*
|
|
803
|
+
* The Console Manifest sandbox uses memory drivers for every facet. Real app
|
|
804
|
+
* boot resolves configured drivers before the Console binds to live runtimes.
|
|
805
|
+
*
|
|
782
806
|
* @param manifest - Manifest snapshot
|
|
783
807
|
* @param now - Clock
|
|
784
808
|
*/
|
|
@@ -29,6 +29,7 @@ export interface DstAmbiguityView {
|
|
|
29
29
|
/** One cron row from `console.clock.list`. */
|
|
30
30
|
export interface ClockCronRecord {
|
|
31
31
|
readonly name: string;
|
|
32
|
+
readonly description?: string;
|
|
32
33
|
readonly status: CronStatus;
|
|
33
34
|
readonly timezone: string;
|
|
34
35
|
readonly overridable: boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { displayLabel } from "./display.ts";
|
|
3
|
+
|
|
4
|
+
describe("displayLabel", () => {
|
|
5
|
+
test("falls back to the raw key when description is absent", () => {
|
|
6
|
+
expect(displayLabel("DATABASE_URL")).toBe("DATABASE_URL");
|
|
7
|
+
expect(displayLabel("DATABASE_URL", undefined)).toBe("DATABASE_URL");
|
|
8
|
+
expect(displayLabel("DATABASE_URL", "")).toBe("DATABASE_URL");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("prefers a non-empty description", () => {
|
|
12
|
+
expect(displayLabel("DATABASE_URL", "Primary database URL")).toBe("Primary database URL");
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-facing label for a technical key when an optional description exists.
|
|
3
|
+
*
|
|
4
|
+
* @param key - Technical identifier (Manifest map key / declaration name)
|
|
5
|
+
* @param description - Optional human description
|
|
6
|
+
*/
|
|
7
|
+
export function displayLabel(key: string, description?: string | null): string {
|
|
8
|
+
return description && description.length > 0 ? description : key;
|
|
9
|
+
}
|