okengine 0.23.0 → 0.23.1
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/package.json +1 -1
- package/site/content/docs/ai/mcp.mdx +1 -1
- package/site/content/docs/elements/channel/index.mdx +4 -4
- package/site/content/docs/elements/channel/receipts.mdx +7 -5
- package/site/content/docs/elements/channel/sms.mdx +2 -1
- package/site/content/docs/elements/flow/index.mdx +22 -18
- package/site/content/docs/elements/signal/index.mdx +15 -14
- package/site/content/docs/plugins/otp.mdx +3 -3
- package/site/content/docs/reference/fx.mdx +12 -4
- package/src/auth/tenants.ts +17 -0
- package/src/cli/doctor-diff.test.ts +29 -1
- package/src/cli/doctor-diff.ts +35 -0
- package/src/cli/manifest-pr-diff.ts +16 -0
- package/src/compiler/effects-fetch.test.ts +77 -0
- package/src/compiler/effects-infer.ts +87 -16
- package/src/compiler/effects-join.test.ts +48 -0
- package/src/compiler/extract.test.ts +28 -0
- package/src/compiler/extract.ts +11 -1
- package/src/compiler/fx-index.ts +58 -4
- package/src/console/index.ts +1 -1
- package/src/console/ui-next/dist/assets/{access-page-DDKkhT9v.js → access-page-C_N4qvai.js} +1 -1
- package/src/console/ui-next/dist/assets/{agent-disclosure-62Xts8Xi.js → agent-disclosure-Cq1TxniW.js} +1 -1
- package/src/console/ui-next/dist/assets/{cache-glyph-DyeoKHKA.js → cache-glyph-BaI4uxZz.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button-DvfpLXsU.js → call-pii-button-DNyK7IIY.js} +1 -1
- package/src/console/ui-next/dist/assets/{collapsible-BY03SeCg.js → collapsible-CRhBJMZR.js} +1 -1
- package/src/console/ui-next/dist/assets/{decisions-page-CgkKlA56.js → decisions-page-D9twj8Xy.js} +1 -1
- package/src/console/ui-next/dist/assets/{duration-tone-Y6HLjyJ5.js → duration-tone-CsHWlVPU.js} +1 -1
- package/src/console/ui-next/dist/assets/{flows-page-DLPfNy-_.js → flows-page-ZaTJAhR9.js} +1 -1
- package/src/console/ui-next/dist/assets/{highlighted-json-D7nNzpgB.js → highlighted-json-DQ-EMAfz.js} +1 -1
- package/src/console/ui-next/dist/assets/{http-method-DdL19zzh.js → http-method-Cn_Fl79s.js} +1 -1
- package/src/console/ui-next/dist/assets/{index-C0lc_s9d.js → index-CmRFeWdT.js} +3 -3
- package/src/console/ui-next/dist/assets/{observability-page-CRlcyLCC.js → observability-page-Cn6R_dXz.js} +1 -1
- package/src/console/ui-next/dist/assets/{replica-lag-Bb3FWUu9.js → replica-lag-Bx6EZhSe.js} +1 -1
- package/src/console/ui-next/dist/assets/{request-meta-DYBMWhX8.js → request-meta-DmdiUrfh.js} +1 -1
- package/src/console/ui-next/dist/assets/{store-page-CcE-SXC8.js → store-page-8P6vGXnn.js} +1 -1
- package/src/console/ui-next/dist/assets/{trace-detail-sheet-CKMSEQ4s.js → trace-detail-sheet-BfGvpFSv.js} +1 -1
- package/src/console/ui-next/dist/assets/{tree-expand-toggle-QOfk0M0H.js → tree-expand-toggle-CWdUHbc0.js} +1 -1
- package/src/console/ui-next/dist/assets/{units-page-DKC1CDDd.js → units-page-CZOFZzay.js} +1 -1
- package/src/console/ui-next/dist/assets/{vault-page-B54v4Yit.js → vault-page-ki_ishmX.js} +1 -1
- package/src/console/ui-next/dist/index.html +1 -1
- package/src/drivers/journal-postgres.test.ts +22 -1
- package/src/drivers/journal-postgres.ts +223 -12
- package/src/drivers/signal-compete.test.ts +234 -0
- package/src/drivers/signal-postgres.ts +39 -19
- package/src/drivers/signal-redis.ts +167 -11
- package/src/drivers/signal-types.ts +12 -0
- package/src/elements/ai/mcp-protocol.ts +5 -6
- package/src/elements/ai/runtime.ts +20 -19
- package/src/elements/channel/runtime.ts +11 -0
- package/src/elements/channel/sql-ledger.ts +251 -0
- package/src/elements/signal/runtime.ts +9 -0
- package/src/elements/signal.test.ts +1 -0
- package/src/elements/store/cache.test.ts +120 -15
- package/src/elements/store/cache.ts +200 -39
- package/src/elements/store/prepare-row.test.ts +6 -2
- package/src/elements/store/sql-session.test.ts +2 -0
- package/src/elements/store/sql-session.ts +95 -9
- package/src/elements/store.ts +1 -0
- package/src/kernel/app.ts +32 -16
- package/src/kernel/auto-cache.test.ts +228 -0
- package/src/kernel/boot-bind/channel.ts +63 -2
- package/src/kernel/boot-bind/honor-config.test.ts +40 -10
- package/src/kernel/boot-bind/signal.ts +61 -25
- package/src/kernel/boot.ts +16 -2
- package/src/kernel/errors-compiler.ts +11 -0
- package/src/kernel/errors-text.ts +16 -0
- package/src/kernel/errors.ts +8 -0
- package/src/kernel/flow.ts +33 -3
- package/src/kernel/fx-call-types.test.ts +32 -0
- package/src/kernel/fx-decide.ts +11 -7
- package/src/kernel/fx-sql-handle.ts +46 -7
- package/src/kernel/fx.test.ts +1 -3
- package/src/kernel/fx.ts +27 -16
- package/src/kernel/idempotency.test.ts +2 -1
- package/src/kernel/journal.test.ts +114 -0
- package/src/kernel/journal.ts +369 -53
- package/src/kernel/pipeline-tenant.ts +5 -1
- package/src/kernel/pipeline.ts +5 -0
- package/src/kernel/signal-tx.ts +28 -0
- package/src/kernel/store-transaction.test.ts +95 -0
- package/src/mcp/docs-server.ts +19 -2
- package/src/mcp/mcp.test.ts +83 -0
- package/src/mcp/protocol.ts +8 -2
- package/src/mcp/server.ts +23 -2
- package/src/mcp/versions.ts +60 -0
- package/src/plugins/index.ts +1 -0
- package/src/plugins/mena.ts +56 -0
|
@@ -110,6 +110,11 @@ export interface InferEffectsOptions {
|
|
|
110
110
|
* never pass `fx` into another function.
|
|
111
111
|
*/
|
|
112
112
|
readonly resolveCallee?: (callee: AstNode, file: string) => FxCalleeResolution;
|
|
113
|
+
/**
|
|
114
|
+
* Flow name of an imported handle in `file`. Absent when inference does
|
|
115
|
+
* not follow imports (unit tests).
|
|
116
|
+
*/
|
|
117
|
+
readonly resolveFlowRef?: (localName: string, file: string) => string | undefined;
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
/** Result of effect inference for one flow body. */
|
|
@@ -129,6 +134,11 @@ export interface InferredEffects {
|
|
|
129
134
|
readonly nondeterministic: boolean;
|
|
130
135
|
/** True when the body references `fx.auth.userId`. */
|
|
131
136
|
readonly readsUserId: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* `fx.fetch` / `fx.send` calls that are not inside an `fx.step` callback.
|
|
139
|
+
* A `durable: true` flow must not call these directly.
|
|
140
|
+
*/
|
|
141
|
+
readonly bareIrreversible: readonly ("fetch" | "send")[];
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
const READ_METHODS = new Set([
|
|
@@ -145,6 +155,10 @@ const READ_METHODS = new Set([
|
|
|
145
155
|
"page",
|
|
146
156
|
"search",
|
|
147
157
|
"raw",
|
|
158
|
+
"innerJoin",
|
|
159
|
+
"leftJoin",
|
|
160
|
+
"rightJoin",
|
|
161
|
+
"fullJoin",
|
|
148
162
|
]);
|
|
149
163
|
|
|
150
164
|
const WRITE_METHODS = new Set([
|
|
@@ -175,6 +189,10 @@ const TABLE_ARG_METHODS = new Set([
|
|
|
175
189
|
"count",
|
|
176
190
|
"page",
|
|
177
191
|
"search",
|
|
192
|
+
"innerJoin",
|
|
193
|
+
"leftJoin",
|
|
194
|
+
"rightJoin",
|
|
195
|
+
"fullJoin",
|
|
178
196
|
]);
|
|
179
197
|
|
|
180
198
|
/**
|
|
@@ -204,7 +222,8 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
|
|
|
204
222
|
name: "liveQuery" | "applySearchEmbedCdc";
|
|
205
223
|
call: CallExpression;
|
|
206
224
|
}> = [];
|
|
207
|
-
const
|
|
225
|
+
const bareIrreversible = new Set<"fetch" | "send">();
|
|
226
|
+
const chains = collectFxChains(options, opaque, userIdRoots, intrinsics, bareIrreversible);
|
|
208
227
|
|
|
209
228
|
for (const { call, chain } of chains) {
|
|
210
229
|
if (chain.rootMethod === "raw") {
|
|
@@ -313,7 +332,13 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
|
|
|
313
332
|
}
|
|
314
333
|
|
|
315
334
|
if (chain.rootMethod === "call" && call === chain.rootCall) {
|
|
316
|
-
const
|
|
335
|
+
const arg = unwrapValueNode(call.arguments[0]);
|
|
336
|
+
const local = identifierName(arg);
|
|
337
|
+
const imported =
|
|
338
|
+
local && options.file && options.resolveFlowRef
|
|
339
|
+
? options.resolveFlowRef(local, options.file)
|
|
340
|
+
: undefined;
|
|
341
|
+
const ref = imported ?? resolveCallTarget(call.arguments[0], options.bindings);
|
|
317
342
|
if (ref) calls.add(ref);
|
|
318
343
|
continue;
|
|
319
344
|
}
|
|
@@ -359,18 +384,28 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
|
|
|
359
384
|
// Skip incomplete chains (`select` before `.from`, `insert` before table) —
|
|
360
385
|
// the sibling call that carries the table arg records the real resource.
|
|
361
386
|
const leaf = resolved.methods[resolved.methods.length - 1]!;
|
|
362
|
-
const
|
|
387
|
+
const tables = tablesFromStoreChain(call, options.bindings, chain);
|
|
388
|
+
const hasTable = tables.length > 0;
|
|
363
389
|
if (!hasTable && (leaf === "select" || leaf === "insert" || leaf === "update")) {
|
|
364
390
|
continue;
|
|
365
391
|
}
|
|
366
392
|
// Intermediate chain links that only forward (where/values/returning)
|
|
367
393
|
// still carry read/write from earlier methods — record once we have a table
|
|
368
394
|
// or a terminal key-based op.
|
|
369
|
-
if (
|
|
395
|
+
if (
|
|
396
|
+
!hasTable &&
|
|
397
|
+
(leaf === "where" || leaf === "values" || leaf === "returning" || leaf === "groupBy")
|
|
398
|
+
) {
|
|
370
399
|
continue;
|
|
371
400
|
}
|
|
372
|
-
|
|
373
|
-
|
|
401
|
+
const resources =
|
|
402
|
+
tables.length > 0
|
|
403
|
+
? tables.map((table) => sqlTableRef(table) as ResourceRef)
|
|
404
|
+
: [resolved.resource];
|
|
405
|
+
for (const resource of resources) {
|
|
406
|
+
if (op === "read" || op === "both") reads.add(resource);
|
|
407
|
+
if (op === "write" || op === "both") writes.add(resource);
|
|
408
|
+
}
|
|
374
409
|
}
|
|
375
410
|
}
|
|
376
411
|
|
|
@@ -407,6 +442,7 @@ export function inferEffects(options: InferEffectsOptions): InferredEffects {
|
|
|
407
442
|
cacheIneligible: usesRaw && !options.hasExplicitEffects,
|
|
408
443
|
nondeterministic: asks.size > 0 || embeds.size > 0 || decides.size > 0,
|
|
409
444
|
readsUserId: userIdRoots.some((node) => containsAuthUserId(node)),
|
|
445
|
+
bareIrreversible: [...bareIrreversible],
|
|
410
446
|
};
|
|
411
447
|
}
|
|
412
448
|
|
|
@@ -610,21 +646,37 @@ function tableFromStoreChain(
|
|
|
610
646
|
bindings: ReadonlyMap<string, InferBinding>,
|
|
611
647
|
chain?: FxChain,
|
|
612
648
|
): string | undefined {
|
|
649
|
+
return tablesFromStoreChain(call, bindings, chain)[0];
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Every table argument on a store chain, including joins.
|
|
654
|
+
*
|
|
655
|
+
* @param call - Any call in the chain
|
|
656
|
+
* @param bindings - Scope bindings
|
|
657
|
+
* @param chain - Precomputed chain when already known
|
|
658
|
+
*/
|
|
659
|
+
function tablesFromStoreChain(
|
|
660
|
+
call: CallExpression,
|
|
661
|
+
bindings: ReadonlyMap<string, InferBinding>,
|
|
662
|
+
chain?: FxChain,
|
|
663
|
+
): string[] {
|
|
613
664
|
const found = tableFromStoreWalk(call, bindings, chain);
|
|
614
|
-
if (found) return found;
|
|
665
|
+
if (found.length > 0) return found;
|
|
615
666
|
if (chain?.aliasLeaf && chain.aliasLeaf !== call) {
|
|
616
667
|
return tableFromStoreWalk(chain.aliasLeaf, bindings);
|
|
617
668
|
}
|
|
618
|
-
return
|
|
669
|
+
return [];
|
|
619
670
|
}
|
|
620
671
|
|
|
621
672
|
function tableFromStoreWalk(
|
|
622
673
|
call: CallExpression,
|
|
623
674
|
bindings: ReadonlyMap<string, InferBinding>,
|
|
624
675
|
chain?: FxChain,
|
|
625
|
-
): string
|
|
676
|
+
): string[] {
|
|
626
677
|
let current: AstNode | undefined = call;
|
|
627
678
|
const seen = new Set<AstNode>();
|
|
679
|
+
const names: string[] = [];
|
|
628
680
|
while (current && current.type === "CallExpression" && !seen.has(current)) {
|
|
629
681
|
seen.add(current);
|
|
630
682
|
const c = current as CallExpression;
|
|
@@ -644,7 +696,8 @@ function tableFromStoreWalk(
|
|
|
644
696
|
const id = identifierName(c.arguments[0]);
|
|
645
697
|
if (id) {
|
|
646
698
|
const binding = bindings.get(id);
|
|
647
|
-
|
|
699
|
+
const tableName = binding?.kind === "table" ? binding.ref : id;
|
|
700
|
+
if (!names.includes(tableName)) names.push(tableName);
|
|
648
701
|
}
|
|
649
702
|
}
|
|
650
703
|
}
|
|
@@ -658,7 +711,7 @@ function tableFromStoreWalk(
|
|
|
658
711
|
}
|
|
659
712
|
break;
|
|
660
713
|
}
|
|
661
|
-
return
|
|
714
|
+
return names;
|
|
662
715
|
}
|
|
663
716
|
|
|
664
717
|
function methodNameOfCall(call: CallExpression): string | undefined {
|
|
@@ -908,7 +961,9 @@ function collectFxChains(
|
|
|
908
961
|
opaque: string[],
|
|
909
962
|
userIdRoots: AstNode[],
|
|
910
963
|
intrinsics: Array<{ name: "liveQuery" | "applySearchEmbedCdc"; call: CallExpression }>,
|
|
964
|
+
bareIrreversible: Set<"fetch" | "send">,
|
|
911
965
|
): ChainHit[] {
|
|
966
|
+
const stepBodies = new Set<AstNode>();
|
|
912
967
|
const hits: ChainHit[] = [];
|
|
913
968
|
const seen = new Set<AstNode>();
|
|
914
969
|
|
|
@@ -918,7 +973,7 @@ function collectFxChains(
|
|
|
918
973
|
aliases: ReadonlyMap<string, ChainAlias>,
|
|
919
974
|
locals: ReadonlyMap<string, AstNode>,
|
|
920
975
|
): void => {
|
|
921
|
-
analyzeFunction(fn, "fx", aliases, locals, file);
|
|
976
|
+
analyzeFunction(fn, "fx", aliases, locals, file, stepBodies.has(fn));
|
|
922
977
|
};
|
|
923
978
|
|
|
924
979
|
const analyzeFunction = (
|
|
@@ -927,13 +982,14 @@ function collectFxChains(
|
|
|
927
982
|
outerAliases: ReadonlyMap<string, ChainAlias>,
|
|
928
983
|
outerLocals: ReadonlyMap<string, AstNode>,
|
|
929
984
|
file: string,
|
|
985
|
+
insideStep: boolean,
|
|
930
986
|
): void => {
|
|
931
987
|
if (seen.has(fn)) return;
|
|
932
988
|
seen.add(fn);
|
|
933
989
|
userIdRoots.push(fn);
|
|
934
990
|
const body = (fn as AstNode & { body?: AstNode }).body;
|
|
935
991
|
if (!body) return;
|
|
936
|
-
analyzeBody(body, door, outerAliases, outerLocals, file);
|
|
992
|
+
analyzeBody(body, door, outerAliases, outerLocals, file, insideStep);
|
|
937
993
|
};
|
|
938
994
|
|
|
939
995
|
const analyzeBody = (
|
|
@@ -942,6 +998,7 @@ function collectFxChains(
|
|
|
942
998
|
outerAliases: ReadonlyMap<string, ChainAlias>,
|
|
943
999
|
outerLocals: ReadonlyMap<string, AstNode>,
|
|
944
1000
|
file: string,
|
|
1001
|
+
insideStep: boolean,
|
|
945
1002
|
): void => {
|
|
946
1003
|
const aliases = new Map(outerAliases);
|
|
947
1004
|
const locals = new Map(outerLocals);
|
|
@@ -951,6 +1008,20 @@ function collectFxChains(
|
|
|
951
1008
|
});
|
|
952
1009
|
for (const call of callsSkippingNested(body)) {
|
|
953
1010
|
const chain = chainFromCall(call, aliases);
|
|
1011
|
+
if (chain?.rootMethod === "step") {
|
|
1012
|
+
for (const arg of call.arguments) {
|
|
1013
|
+
const fn = unwrapValue(arg);
|
|
1014
|
+
if (fn && isFunctionNode(fn)) stepBodies.add(fn);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (
|
|
1018
|
+
!insideStep &&
|
|
1019
|
+
chain &&
|
|
1020
|
+
call === chain.rootCall &&
|
|
1021
|
+
(chain.rootMethod === "fetch" || chain.rootMethod === "send")
|
|
1022
|
+
) {
|
|
1023
|
+
bareIrreversible.add(chain.rootMethod);
|
|
1024
|
+
}
|
|
954
1025
|
if (chain) {
|
|
955
1026
|
hits.push({ call, chain });
|
|
956
1027
|
continue;
|
|
@@ -961,7 +1032,7 @@ function collectFxChains(
|
|
|
961
1032
|
if (seen.has(nested)) continue;
|
|
962
1033
|
const shadows = functionParams(nested).some((param) => paramIdentifierName(param) === door);
|
|
963
1034
|
if (shadows) continue;
|
|
964
|
-
analyzeFunction(nested, door, aliases, locals, file);
|
|
1035
|
+
analyzeFunction(nested, door, aliases, locals, file, insideStep || stepBodies.has(nested));
|
|
965
1036
|
}
|
|
966
1037
|
};
|
|
967
1038
|
|
|
@@ -972,9 +1043,9 @@ function collectFxChains(
|
|
|
972
1043
|
if (second && paramIdentifierName(second) !== "fx") {
|
|
973
1044
|
opaque.push("the do callback's second parameter is not named fx");
|
|
974
1045
|
}
|
|
975
|
-
analyzeFunction(options.doNode, "fx", new Map(), new Map(), startFile);
|
|
1046
|
+
analyzeFunction(options.doNode, "fx", new Map(), new Map(), startFile, false);
|
|
976
1047
|
} else {
|
|
977
|
-
analyzeBody(options.doNode, "fx", new Map(), new Map(), startFile);
|
|
1048
|
+
analyzeBody(options.doNode, "fx", new Map(), new Map(), startFile, false);
|
|
978
1049
|
}
|
|
979
1050
|
|
|
980
1051
|
return hits;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Joins and groupBy list every source table under the flow's reads.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { extractFromSources } from "./extract.ts";
|
|
7
|
+
|
|
8
|
+
describe("extractManifest — joins", () => {
|
|
9
|
+
test("innerJoin and groupBy record every table", async () => {
|
|
10
|
+
const manifest = await extractFromSources({
|
|
11
|
+
"src/app.ts": `
|
|
12
|
+
import { oke } from "okengine";
|
|
13
|
+
export const app = oke({ name: "joins" });
|
|
14
|
+
`,
|
|
15
|
+
"src/flows/orders.ts": `
|
|
16
|
+
import { on, flow, http, store, field } from "okengine";
|
|
17
|
+
import { eq, count } from "drizzle-orm";
|
|
18
|
+
|
|
19
|
+
export const db = store.sql("db");
|
|
20
|
+
export const orders = store.schema.table("orders", {
|
|
21
|
+
id: field.text().primaryKey(),
|
|
22
|
+
customerId: field.text(),
|
|
23
|
+
});
|
|
24
|
+
export const customers = store.schema.table("customers", {
|
|
25
|
+
id: field.text().primaryKey(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const totals = on(
|
|
29
|
+
http.get("/totals"),
|
|
30
|
+
flow("orders.totals", {
|
|
31
|
+
do: async (_input, fx) => {
|
|
32
|
+
return fx
|
|
33
|
+
.store(db)
|
|
34
|
+
.select({ n: count() })
|
|
35
|
+
.from(orders)
|
|
36
|
+
.innerJoin(customers, eq(orders.customerId, customers.id))
|
|
37
|
+
.groupBy(customers.id);
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
`,
|
|
42
|
+
});
|
|
43
|
+
expect(manifest.flows?.["orders.totals"]?.effects?.reads?.slice().sort()).toEqual([
|
|
44
|
+
"sql:customers",
|
|
45
|
+
"sql:orders",
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -1577,6 +1577,34 @@ export const ping = flow({ do: () => ({ ok: true }) });
|
|
|
1577
1577
|
expect(manifest.flows?.["notes.ping"]).toBeDefined();
|
|
1578
1578
|
});
|
|
1579
1579
|
|
|
1580
|
+
test("fx.call of an imported flow handle records that flow's name", async () => {
|
|
1581
|
+
const tasks = `
|
|
1582
|
+
import { on, flow, http } from "okengine";
|
|
1583
|
+
export const create = on(
|
|
1584
|
+
http.post("/tasks"),
|
|
1585
|
+
flow("tasks.create", { do: async () => ({ id: "1" }) }),
|
|
1586
|
+
);
|
|
1587
|
+
`;
|
|
1588
|
+
const forms = `
|
|
1589
|
+
import { on, flow, http } from "okengine";
|
|
1590
|
+
import { create as createTask } from "../tasks/index.ts";
|
|
1591
|
+
export const submit = on(
|
|
1592
|
+
http.post("/forms/:id/submit"),
|
|
1593
|
+
flow("forms.submit", {
|
|
1594
|
+
do: async (_input, fx) => {
|
|
1595
|
+
await fx.call(createTask, { title: "SSO" });
|
|
1596
|
+
return { ok: true };
|
|
1597
|
+
},
|
|
1598
|
+
}),
|
|
1599
|
+
);
|
|
1600
|
+
`;
|
|
1601
|
+
const manifest = await extractFromSources({
|
|
1602
|
+
"src/flows/tasks/index.ts": tasks,
|
|
1603
|
+
"src/flows/forms/index.ts": forms,
|
|
1604
|
+
});
|
|
1605
|
+
expect(manifest.flows?.["forms.submit"]?.effects?.calls).toEqual(["tasks.create"]);
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1580
1608
|
test("effects are identical for the same do body in a tree file vs a barrel", async () => {
|
|
1581
1609
|
const doBody = `
|
|
1582
1610
|
const rows = await fx.store(db).select().from(notes);
|
package/src/compiler/extract.ts
CHANGED
|
@@ -2194,6 +2194,7 @@ function registerFlow(args: {
|
|
|
2194
2194
|
flowName: name,
|
|
2195
2195
|
file: args.file.path,
|
|
2196
2196
|
resolveCallee: (callee, file) => args.scope.fxIndex.resolve(file, callee),
|
|
2197
|
+
resolveFlowRef: (localName, file) => args.scope.fxIndex.flowRef(file, localName),
|
|
2197
2198
|
})
|
|
2198
2199
|
: {
|
|
2199
2200
|
effects: {} as Effects,
|
|
@@ -2202,6 +2203,7 @@ function registerFlow(args: {
|
|
|
2202
2203
|
cacheIneligible: false,
|
|
2203
2204
|
nondeterministic: false,
|
|
2204
2205
|
readsUserId: false,
|
|
2206
|
+
bareIrreversible: [] as ("fetch" | "send")[],
|
|
2205
2207
|
};
|
|
2206
2208
|
|
|
2207
2209
|
let effects: Effects | undefined;
|
|
@@ -2262,7 +2264,15 @@ function registerFlow(args: {
|
|
|
2262
2264
|
const line = lineAt(args.file.source, args.flowCall.start ?? 0);
|
|
2263
2265
|
flow.source = `${args.file.path}:${line}`;
|
|
2264
2266
|
|
|
2265
|
-
if (boolProp(opts, "durable"))
|
|
2267
|
+
if (boolProp(opts, "durable")) {
|
|
2268
|
+
flow.durable = true;
|
|
2269
|
+
const bare = inferred.bareIrreversible[0];
|
|
2270
|
+
if (bare !== undefined) {
|
|
2271
|
+
throw new Error(
|
|
2272
|
+
`OKE1901: durable flow "${name}" calls fx.${bare} outside fx.step. Wrap fx.fetch and fx.send in fx.step.`,
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2266
2276
|
assertDecisionPlacement(name, flow, args.scope);
|
|
2267
2277
|
if (typeof liveFromTrigger === "string") {
|
|
2268
2278
|
flow.live = liveFromTrigger;
|
package/src/compiler/fx-index.ts
CHANGED
|
@@ -26,6 +26,14 @@ export interface FxIndex {
|
|
|
26
26
|
* @param callee - Call callee node
|
|
27
27
|
*/
|
|
28
28
|
resolve(file: string, callee: AstNode): FxCalleeResolution;
|
|
29
|
+
/**
|
|
30
|
+
* Flow name behind an imported handle (`import { create as createTask }`).
|
|
31
|
+
* Absent when `localName` is not a named import of a `flow()` / `on()` export.
|
|
32
|
+
*
|
|
33
|
+
* @param file - File that contains the `fx.call`
|
|
34
|
+
* @param localName - Local binding (`createTask`)
|
|
35
|
+
*/
|
|
36
|
+
flowRef(file: string, localName: string): string | undefined;
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
interface NamedImport {
|
|
@@ -61,6 +69,8 @@ interface FileFx {
|
|
|
61
69
|
readonly functions: Map<string, AstNode>;
|
|
62
70
|
readonly imports: Map<string, ImportBinding>;
|
|
63
71
|
readonly exports: Map<string, LocalExport | Reexport>;
|
|
72
|
+
/** Local binding → `flow("name")` / `on(…, flow("name"))` literal. */
|
|
73
|
+
readonly flowNames: Map<string, string>;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
const INTRINSIC_SOURCES: Readonly<
|
|
@@ -176,7 +186,29 @@ export function buildFxIndex(
|
|
|
176
186
|
return resolveExport(target, imported.imported, seen);
|
|
177
187
|
}
|
|
178
188
|
|
|
189
|
+
function flowExportName(file: string, exportName: string, seen: Set<string>): string | undefined {
|
|
190
|
+
const key = `${file}#flow:${exportName}`;
|
|
191
|
+
if (seen.has(key)) return undefined;
|
|
192
|
+
seen.add(key);
|
|
193
|
+
const indexed = byFile.get(file);
|
|
194
|
+
if (!indexed) return undefined;
|
|
195
|
+
const exported = indexed.exports.get(exportName);
|
|
196
|
+
if (!exported) return indexed.flowNames.get(exportName);
|
|
197
|
+
if (exported.kind === "local") return indexed.flowNames.get(exported.binding);
|
|
198
|
+
const target = resolveModule(file, exported.from);
|
|
199
|
+
if (!target) return undefined;
|
|
200
|
+
return flowExportName(target, exported.imported, seen);
|
|
201
|
+
}
|
|
202
|
+
|
|
179
203
|
return {
|
|
204
|
+
flowRef(file, localName) {
|
|
205
|
+
const from = normalizePosix(file);
|
|
206
|
+
const imported = byFile.get(from)?.imports.get(localName);
|
|
207
|
+
if (!imported || imported.kind !== "named") return undefined;
|
|
208
|
+
const target = resolveModule(from, imported.from);
|
|
209
|
+
if (!target) return undefined;
|
|
210
|
+
return flowExportName(target, imported.imported, new Set());
|
|
211
|
+
},
|
|
180
212
|
resolve(file, callee) {
|
|
181
213
|
const from = normalizePosix(file);
|
|
182
214
|
const seen = new Set<string>();
|
|
@@ -212,9 +244,10 @@ function indexFile(program: AstNode): FileFx {
|
|
|
212
244
|
const functions = new Map<string, AstNode>();
|
|
213
245
|
const imports = new Map<string, ImportBinding>();
|
|
214
246
|
const exports = new Map<string, LocalExport | Reexport>();
|
|
247
|
+
const flowNames = new Map<string, string>();
|
|
215
248
|
const body = (program as AstNode & { body?: AstNode[] }).body ?? [];
|
|
216
|
-
for (const stmt of body) indexStmt(stmt, functions, imports, exports);
|
|
217
|
-
return { functions, imports, exports };
|
|
249
|
+
for (const stmt of body) indexStmt(stmt, functions, imports, exports, flowNames);
|
|
250
|
+
return { functions, imports, exports, flowNames };
|
|
218
251
|
}
|
|
219
252
|
|
|
220
253
|
function indexStmt(
|
|
@@ -222,6 +255,7 @@ function indexStmt(
|
|
|
222
255
|
functions: Map<string, AstNode>,
|
|
223
256
|
imports: Map<string, ImportBinding>,
|
|
224
257
|
exports: Map<string, LocalExport | Reexport>,
|
|
258
|
+
flowNames: Map<string, string>,
|
|
225
259
|
): void {
|
|
226
260
|
if (stmt.type === "ImportDeclaration") {
|
|
227
261
|
indexImport(stmt, imports);
|
|
@@ -232,7 +266,7 @@ function indexStmt(
|
|
|
232
266
|
return;
|
|
233
267
|
}
|
|
234
268
|
if (stmt.type === "VariableDeclaration") {
|
|
235
|
-
rememberVars(stmt, functions, exports, false);
|
|
269
|
+
rememberVars(stmt, functions, exports, flowNames, false);
|
|
236
270
|
return;
|
|
237
271
|
}
|
|
238
272
|
if (stmt.type === "ExportDefaultDeclaration") {
|
|
@@ -247,7 +281,7 @@ function indexStmt(
|
|
|
247
281
|
const decl = (stmt as AstNode & { declaration?: AstNode | null }).declaration;
|
|
248
282
|
const source = stringArg((stmt as AstNode & { source?: AstNode }).source);
|
|
249
283
|
if (decl?.type === "FunctionDeclaration") rememberFunction(decl, functions, exports, true);
|
|
250
|
-
if (decl?.type === "VariableDeclaration") rememberVars(decl, functions, exports, true);
|
|
284
|
+
if (decl?.type === "VariableDeclaration") rememberVars(decl, functions, exports, flowNames, true);
|
|
251
285
|
const specifiers = (stmt as AstNode & { specifiers?: AstNode[] }).specifiers ?? [];
|
|
252
286
|
for (const spec of specifiers) {
|
|
253
287
|
const local = exportedLocalName(spec);
|
|
@@ -309,6 +343,7 @@ function rememberVars(
|
|
|
309
343
|
decl: AstNode,
|
|
310
344
|
functions: Map<string, AstNode>,
|
|
311
345
|
exports: Map<string, LocalExport | Reexport>,
|
|
346
|
+
flowNames: Map<string, string>,
|
|
312
347
|
exportIt: boolean,
|
|
313
348
|
): void {
|
|
314
349
|
const declarations = (decl as AstNode & { declarations?: AstNode[] }).declarations ?? [];
|
|
@@ -317,10 +352,29 @@ function rememberVars(
|
|
|
317
352
|
if (!name) continue;
|
|
318
353
|
const init = (item as AstNode & { init?: AstNode | null }).init;
|
|
319
354
|
if (init && isFunctionNode(unwrapValue(init))) functions.set(name, unwrapValue(init));
|
|
355
|
+
const flowName = init ? flowLiteralName(unwrapValue(init)) : undefined;
|
|
356
|
+
if (flowName) flowNames.set(name, flowName);
|
|
320
357
|
if (exportIt) exports.set(name, { kind: "local", binding: name });
|
|
321
358
|
}
|
|
322
359
|
}
|
|
323
360
|
|
|
361
|
+
/**
|
|
362
|
+
* `flow("name")` / `call("name")` / `on(trigger, flow("name"))` literal.
|
|
363
|
+
*
|
|
364
|
+
* @param node - Initializer
|
|
365
|
+
*/
|
|
366
|
+
function flowLiteralName(node: AstNode): string | undefined {
|
|
367
|
+
if (node.type !== "CallExpression") return undefined;
|
|
368
|
+
const call = node as AstNode & { callee: AstNode; arguments?: AstNode[] };
|
|
369
|
+
const callee = identifierName(call.callee);
|
|
370
|
+
if (callee === "flow" || callee === "call") return stringArg(call.arguments?.[0]);
|
|
371
|
+
if (callee === "on") {
|
|
372
|
+
const body = call.arguments?.[1];
|
|
373
|
+
return body ? flowLiteralName(unwrapValue(body)) : undefined;
|
|
374
|
+
}
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
|
|
324
378
|
function exportedLocalName(spec: AstNode): string | undefined {
|
|
325
379
|
return (
|
|
326
380
|
identifierName((spec as AstNode & { local?: AstNode }).local) ??
|
package/src/console/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Console — operator plane UI + server (port 6533).
|
|
2
|
+
* Console — operator plane UI + server (port 6533). Public import: `okengine/console`.
|
|
3
3
|
*
|
|
4
4
|
* Built on `createClient<ConsoleApp>`. Every action is a real flow through `fx`;
|
|
5
5
|
* the audit log is the trace (docs/spec/console.md).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{r as t}from"./react-dom-Ddte4I-Q.js";import{A as n}from"./xyflow-yApv7D4e.js";import{$n as r,Cr as i,E as a,F as o,I as s,Qn as c,R as l,c as u,i as d,l as f,o as p,rr as m,s as h,tr as g,u as _,x as v}from"./shortcut-keys-DTE1sjTC.js";import{C as y,D as b,E as x,F as S,I as C,L as w,M as ee,N as T,O as E,P as D,T as O,W as k,_ as A,a as j,b as M,c as N,d as P,f as F,g as I,i as L,m as R,o as z,p as te,u as B,w as V,x as H}from"./explorer-chrome-Dslh2yE8.js";import{i as ne,t as re}from"./http-method-
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{r as t}from"./react-dom-Ddte4I-Q.js";import{A as n}from"./xyflow-yApv7D4e.js";import{$n as r,Cr as i,E as a,F as o,I as s,Qn as c,R as l,c as u,i as d,l as f,o as p,rr as m,s as h,tr as g,u as _,x as v}from"./shortcut-keys-DTE1sjTC.js";import{C as y,D as b,E as x,F as S,I as C,L as w,M as ee,N as T,O as E,P as D,T as O,W as k,_ as A,a as j,b as M,c as N,d as P,f as F,g as I,i as L,m as R,o as z,p as te,u as B,w as V,x as H}from"./explorer-chrome-Dslh2yE8.js";import{i as ne,t as re}from"./http-method-Cn_Fl79s.js";import{a as ie,c as ae,h as oe,i as se,n as ce,o as le,r as ue,s as U,t as de}from"./element-icons-C6t8Vlml.js";import{t as fe}from"./UserIcon-DaE2PB5_.js";import{n as pe,t as W}from"./copy-inline-button-BTjPVjRT.js";import{Mt as G,bt as me,ht as he}from"./index-CmRFeWdT.js";import{t as ge}from"./explorer-empty-CIVg7Jgb.js";import{a as K,c as _e,l as q,n as J,o as ve,r as ye,s as be,t as xe}from"./detail-header-Dk5LipF6.js";import{t as Se}from"./confirm-sheet-DZfuJXP4.js";var Ce=[[`path`,{d:`M5 21H19`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`0`}],[`path`,{d:`M12.125 12.75H12M12.25 12.75C12.25 12.8881 12.1381 13 12 13C11.8619 13 11.75 12.8881 11.75 12.75C11.75 12.6119 11.8619 12.5 12 12.5C12.1381 12.5 12.25 12.6119 12.25 12.75Z`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`1`}],[`path`,{d:`M14.9152 7.61089L13.8078 5.38179C13.019 3.79393 12.6246 3 12 3C11.3754 3 10.981 3.79393 10.1922 5.38179L9.08483 7.61089C8.58107 8.62494 8.32919 9.13197 7.87976 9.24608C7.8485 9.25401 7.81689 9.26043 7.78503 9.26533C7.32682 9.3357 6.89919 8.96678 6.04393 8.22895C4.0124 6.47635 2.99663 5.60004 2.38034 5.94899C2.34045 5.97157 2.30213 5.99686 2.26565 6.02467C1.70197 6.45439 2.09541 7.74136 2.88229 10.3153L4.04783 14.1279C4.47098 15.5121 4.68255 16.2042 5.21787 16.6021C5.75318 17 6.47261 17 7.91147 17L16.0886 16.9999C17.5274 16.9999 18.2468 16.9999 18.7821 16.602C19.3175 16.2041 19.529 15.512 19.9522 14.1279L21.1177 10.3153C21.9046 7.74137 22.298 6.4544 21.7344 6.02468C21.6979 5.99687 21.6595 5.97158 21.6197 5.94899C21.0034 5.60006 19.9876 6.47636 17.9561 8.22896C17.1008 8.96679 16.6732 9.3357 16.215 9.26533C16.1831 9.26043 16.1515 9.25401 16.1202 9.24607C15.6708 9.13197 15.4189 8.62494 14.9152 7.61089Z`,stroke:`currentColor`,strokeLinecap:`round`,strokeLinejoin:`round`,strokeWidth:`1.5`,key:`2`}]],Y=e(t(),1);function we(e,t){if(e==null)return`never`;let n=e-t;if(Math.abs(n)<6e4)return`just now`;let r=Ee(Math.abs(n));return n<0?`${r} ago`:`in ${r}`}function Te(e,t){return e==null?`never`:e<=t?`expired`:we(e,t)}function Ee(e){if(e<36e5)return`${Math.floor(e/6e4)}m`;if(e<864e5)return`${Math.floor(e/36e5)}h`;let t=Math.floor(e/864e5);return t<100?`${t}d`:`${Math.floor(t/30)}mo`}var X=864e5;function De(e){let t=/^(\d+)(ms|s|m|h|d)$/.exec(e.trim());if(!t)return 0;let n=Number(t[1]);switch(t[2]){case`ms`:return n;case`s`:return n*1e3;case`m`:return n*6e4;case`h`:return n*36e5;case`d`:return n*X;default:return 0}}function Oe(e,t,n=``){if(e===`never`)return null;if(e===`30d`)return t+30*X;if(e===`90d`)return t+90*X;let r=De(n);return r>0?t+r:null}var Z=36e5;function ke(e,t){if(e==null)return{choice:`never`,custom:``};let n=e-t;return n<=0?{choice:`never`,custom:``}:Math.abs(n-30*X)<Z?{choice:`30d`,custom:``}:Math.abs(n-90*X)<Z?{choice:`90d`,custom:``}:{choice:`custom`,custom:Me(n)}}var Ae=7*X;function je(e,t){if(e==null||e<=t)return{choice:`90d`,custom:``};let n=e-t;return Math.abs(n-30*X)<=Ae?{choice:`30d`,custom:``}:Math.abs(n-90*X)<=Ae?{choice:`90d`,custom:``}:{choice:`custom`,custom:Me(n)}}function Me(e){return e<=0?``:e>=X&&e%X===0?`${e/X}d`:e>=Z&&e%Z===0?`${e/Z}h`:e>=6e4&&e%6e4==0?`${e/6e4}m`:e>=X?`${Math.max(1,Math.round(e/X))}d`:e>=Z?`${Math.max(1,Math.round(e/Z))}h`:`${Math.max(1,Math.round(e/6e4))}m`}var Ne=[`s`,`m`,`h`,`d`,`ms`],Pe={s:`Seconds`,m:`Minutes`,h:`Hours`,d:`Days`,ms:`Milliseconds`};function Fe(e,t,n){let r=e.trim(),i=t.trim();if(r.length===0&&i.length===0)return null;if(r.length===0||i.length===0||!/^\d+$/.test(r)||!/^\d+$/.test(i))return;let a=Number(r),o=Number(i);if(a<=0||o<=0)return;let s=`${o}${n}`;if(!(De(s)<=0))return{max:a,per:s}}function Ie(e){let t=/^(\d+)(ms|s|m|h|d)$/.exec(e.trim());return t?{count:t[1],unit:t[2]}:{count:``,unit:`m`}}function Le(e){let t=ze(e);return t.length===0?null:Be(t)?`ip`:Ve(t)?`host`:null}function Re(e){let t=new Set,n=[];for(let r of e.split(/[\s,]+/)){let e=ze(r);e.length===0||t.has(e)||Le(e)!=null&&(t.add(e),n.push(e))}return n}function ze(e){let t=e.trim().toLowerCase();if(t=t.replace(/^https?:\/\//,``),t.startsWith(`[`)&&t.includes(`]`)){let e=t.indexOf(`]`),n=t.slice(1,e),r=t.slice(e+1);t=r.startsWith(`:`)||r.startsWith(`/`)?n:`${n}${r}`}let n=t.indexOf(`/`);return n>=0&&(t=t.slice(0,n)),t.endsWith(`.`)&&(t=t.slice(0,-1)),t}function Be(e){return/^\d{1,3}(?:\.\d{1,3}){3}$/.test(e)?e.split(`.`).every(e=>Number(e)<=255):!e.includes(`:`)||!/^[0-9a-f:]+$/.test(e)||e.split(`::`).length>2?!1:e.split(`:`).filter(e=>e.length>0).length>=1}function Ve(e){if(e===`localhost`)return!0;if(e.length>253||e.includes(`:`))return!1;let t=e.split(`.`);return t.length<2?!1:t.every((e,n)=>!(e.length===0||e.length>63||!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(e)||n===t.length-1&&!/^[a-z]{2,63}$/.test(e)))}function He(e){let t=[],n=new Map;for(let r of e){let e=r.indexOf(`:`),i=e<=0?r:r.slice(0,e),a=e<=0?r:r.slice(e+1),o=n.get(i);if(o){o.push({scope:r,action:a});continue}t.push(i),n.set(i,[{scope:r,action:a}])}return t.map(e=>({group:e,items:n.get(e)??[]}))}var Q=n(),Ue=`GET /secure
|
|
2
2
|
Authorization: Bearer <secret>`,We=`{ "ok": true }`;function Ge({keyRow:e,blast:t,now:n,leading:i,onEdit:a,onRefresh:o,onRevoke:s,onRotate:l,rotatePending:u}){let d=e.revokedAt!==null,f=e.expiresAt!==null&&e.expiresAt<=n,p=t?.lastUsedAt??e.lastUsedAt,m=t?.callVolume??0,h=Te(e.expiresAt,n),g=we(p,n),_=e.rateLimit?`${e.rateLimit.max} / ${e.rateLimit.per}`:`none`,y=(0,Y.useMemo)(()=>He(e.scopes),[e.scopes]),x=e.creatorScopes.length,S=x>0&&e.scopes.length<x;return(0,Q.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col overflow-y-auto`,"data-slot":`access-detail`,"aria-label":`Access key detail`,"aria-live":`polite`,children:[(0,Q.jsx)(xe,{dataSlot:`access-detail-header`,leading:i,icon:(0,Q.jsx)(r,{icon:oe,className:`size-4`}),title:e.name,badge:(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(`span`,{className:`inline-flex items-center gap-1 font-mono text-[10px] font-medium tracking-[0.08em] text-muted-foreground uppercase`,children:[(0,Q.jsx)(r,{icon:e.plane===`operator`?Ce:fe,className:`size-3`,"aria-hidden":!0}),e.plane]}),d?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-destructive`,children:`revoked`}):null,f&&!d?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-amber-700 dark:text-amber-400`,children:`expired`}):null,e.unused90d&&!d&&!f?(0,Q.jsx)(`span`,{className:`font-mono text-[10px] text-amber-700 dark:text-amber-400`,children:`unused`}):null]}),subtitle:(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(`code`,{className:`min-w-0 truncate font-mono text-[11px] leading-none text-foreground/80`,children:e.id}),(0,Q.jsx)(W,{value:e.id,label:`Copy ${e.id}`})]}),actions:(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(U,{label:`Rename or re-attenuate scopes`,className:`flex self-stretch`,children:(0,Q.jsx)(v,{type:`button`,variant:`ghost`,size:`sm`,className:`h-full rounded-none`,disabled:d,onClick:a,children:`Edit`})}),(0,Q.jsx)(U,{label:`Reset expiry from now. The secret stays the same.`,className:`flex self-stretch`,children:(0,Q.jsx)(v,{type:`button`,variant:`ghost`,size:`sm`,className:`h-full rounded-none`,"data-slot":`access-refresh`,disabled:d,onClick:o,children:`Refresh`})}),(0,Q.jsx)(U,{label:`Mint a new secret. The current secret stops working after residual TTL.`,className:`flex self-stretch`,children:(0,Q.jsx)(v,{type:`button`,variant:`ghost`,size:`sm`,className:`h-full rounded-none`,disabled:u||d,onClick:l,children:`Rotate`})}),(0,Q.jsx)(U,{label:`Irreversible. Residual sessions may continue for the access TTL.`,className:`flex self-stretch`,children:(0,Q.jsx)(v,{type:`button`,variant:`destructive`,size:`sm`,className:`h-full rounded-none`,disabled:d,onClick:s,children:`Revoke`})})]})}),(0,Q.jsx)(Ke,{revoked:d,expired:f,unused:e.unused90d,residual:t?.residualAccessNote}),(0,Q.jsxs)(`div`,{className:H,"data-slot":`access-briefing`,role:`group`,"aria-label":`Usage`,children:[(0,Q.jsx)(Xe,{label:`calls`,value:String(m)}),(0,Q.jsx)(Xe,{label:`used`,value:g,hint:p?new Date(p).toISOString():`No recorded call`,warn:p==null&&!d}),(0,Q.jsx)(Xe,{label:`expires`,value:h,hint:e.expiresAt?new Date(e.expiresAt).toISOString():`No expiry`,warn:f&&!d}),(0,Q.jsx)(Xe,{label:`rate`,value:_})]}),(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Scopes`,children:[(0,Q.jsx)($,{title:`Scopes`,meta:S?`${e.scopes.length} of ${x}`:String(e.scopes.length),children:e.scopes.length>0?(0,Q.jsx)(W,{value:e.scopes.join(`
|
|
3
3
|
`),label:`Copy scopes`}):null}),y.length===0?(0,Q.jsx)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:`No scopes granted.`}):(0,Q.jsx)(`ul`,{"data-slot":`access-scopes`,children:y.map(e=>(0,Q.jsxs)(`li`,{className:A,"aria-label":e.group,children:[(0,Q.jsx)(`span`,{className:c(b,`w-[5.5rem] shrink-0`),children:e.group}),(0,Q.jsx)(`div`,{className:`flex min-w-0 flex-1 flex-wrap items-center gap-x-2`,children:e.items.map(e=>(0,Q.jsx)(`span`,{title:e.scope,className:`font-mono text-[11px] text-foreground`,children:e.action},e.scope))}),(0,Q.jsx)(Ze,{value:e.items.map(e=>e.scope).join(`
|
|
4
4
|
`),label:`Copy ${e.group} scopes`})]},e.group))})]}),(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Key`,children:[(0,Q.jsx)($,{title:`Key`}),(0,Q.jsxs)(`dl`,{"data-slot":`access-key-facts`,children:[(0,Q.jsx)(Qe,{label:`Issuer`,value:e.creatorId,copy:e.creatorId,copyLabel:`Copy ${e.creatorId}`}),(0,Q.jsx)(Qe,{label:`Created`,value:we(e.createdAt,n),hint:new Date(e.createdAt).toISOString()}),(0,Q.jsx)(Qe,{label:`Expires`,value:h,hint:e.expiresAt?new Date(e.expiresAt).toISOString():void 0,warn:f&&!d}),(0,Q.jsx)(Qe,{label:`Rate`,value:_})]})]}),(0,Q.jsx)(qe,{entries:e.ipAllowlist}),(0,Q.jsx)(Je,{sourceAddresses:t?.sourceAddresses??[],residual:t?.residualAccessNote,lastUsed:p,now:n}),(0,Q.jsx)(Ye,{scopes:e.scopes})]})}function Ke({revoked:e,expired:t,unused:n,residual:r}){return e?(0,Q.jsx)(`section`,{className:c(H,`border-destructive/25 bg-destructive/5`),"aria-label":`Revoked`,role:`status`,children:(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center px-2 text-[12px] text-destructive`,children:[`Revoked`,(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[` `,`· `,r??`This secret no longer authenticates.`]})]})}):t?(0,Q.jsx)(`section`,{className:c(H,`border-amber-500/25 bg-amber-500/5`),"aria-label":`Expired`,role:`status`,children:(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center px-2 text-[12px] text-amber-900 dark:text-amber-300`,children:[`Expired`,r?(0,Q.jsxs)(`span`,{className:`text-muted-foreground`,children:[` · `,r]}):null]})}):n?(0,Q.jsx)(`section`,{className:c(H,`border-amber-500/25 bg-amber-500/5`),"aria-label":`Unused`,role:`status`,children:(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center px-2 text-[12px] text-amber-900 dark:text-amber-300`,children:[`No calls in 90 days`,(0,Q.jsx)(`span`,{className:`text-muted-foreground`,children:` · rotate or revoke`})]})}):null}function qe({entries:e}){return(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Allow`,children:[(0,Q.jsx)($,{title:`Allow`,meta:e.length>0?String(e.length):`any`}),e.length===0?(0,Q.jsx)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:`Any origin.`}):(0,Q.jsx)(`ul`,{"data-slot":`access-allow`,children:e.map(e=>{let t=Le(e);return(0,Q.jsxs)(`li`,{className:A,children:[(0,Q.jsx)(`span`,{className:c(b,`w-[5.5rem] shrink-0`),children:t===`ip`?`IP`:t===`host`?`Host`:`—`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-[11px]`,children:e}),(0,Q.jsx)(Ze,{value:e,label:`Copy ${e}`})]},e)})})]})}function Je({sourceAddresses:e,residual:t,lastUsed:n,now:r}){return(0,Q.jsxs)(`section`,{className:`shrink-0 border-b border-border/60`,"aria-label":`Usage`,children:[(0,Q.jsx)($,{title:`Usage`,meta:n?we(n,r):`never`}),e.length===0?(0,Q.jsx)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:`No observed source IPs.`}):(0,Q.jsx)(`ul`,{"data-slot":`access-sources`,children:e.map(e=>(0,Q.jsxs)(`li`,{className:A,children:[(0,Q.jsx)(`span`,{className:c(b,`w-[5.5rem] shrink-0`),children:`IP`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-[11px]`,children:e}),(0,Q.jsx)(Ze,{value:e,label:`Copy ${e}`})]},e))}),t?(0,Q.jsx)(`p`,{className:`border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground`,children:t}):null]})}function Ye({scopes:e}){return(0,Q.jsxs)(`section`,{"data-slot":`access-call-example`,children:[(0,Q.jsx)($,{title:`Request`,children:(0,Q.jsx)(W,{value:Ue,label:`Copy request`})}),(0,Q.jsxs)(`div`,{className:`flex min-w-0 border-b border-border/60`,children:[(0,Q.jsx)(`div`,{className:c(`w-1 shrink-0 self-stretch`,ne(`GET`)),"aria-hidden":!0}),(0,Q.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,Q.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2 px-2.5 py-2`,children:[(0,Q.jsx)(`span`,{className:c(`shrink-0 font-mono text-[10px] font-semibold tracking-[0.08em] uppercase`,E(re(`GET`))),children:`GET`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-xs text-foreground select-all`,children:`/secure`})]}),(0,Q.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2 border-t border-border/60 px-2.5 py-2`,children:[(0,Q.jsx)(`span`,{className:`w-[7.5rem] shrink-0 truncate font-mono text-[11px] font-medium text-sky-600 dark:text-sky-400`,children:`Authorization`}),(0,Q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-[11px] text-muted-foreground`,children:`Bearer <secret>`})]})]})]}),(0,Q.jsx)($,{title:`Response`,children:(0,Q.jsx)(W,{value:We,label:`Copy response`})}),(0,Q.jsxs)(`div`,{className:`flex min-w-0 border-b border-border/60`,children:[(0,Q.jsx)(`div`,{className:`w-1 shrink-0 self-stretch bg-emerald-500`,"aria-hidden":!0}),(0,Q.jsx)(`p`,{className:`min-w-0 flex-1 px-2.5 py-2 font-mono text-[11px] text-foreground/80 select-all`,children:We})]}),(0,Q.jsxs)(`p`,{className:`px-2 py-1.5 text-[11px] text-muted-foreground`,children:[`Seed the Bearer secret from create / rotate. The key is the issuer; gates see`,` `,e.join(`, `)||`its scopes`,`.`]})]})}function $({title:e,meta:t,children:n}){return(0,Q.jsxs)(`header`,{className:c(H,`items-center gap-2 px-2`),children:[(0,Q.jsx)(`h3`,{className:c(b,`shrink-0 leading-none`),children:e}),t?(0,Q.jsx)(`span`,{className:c(N,!n&&`ml-auto`),children:t}):null,n?(0,Q.jsx)(`div`,{className:`ml-auto flex items-center`,children:n}):null]})}function Xe({label:e,value:t,hint:n,warn:r=!1}){return(0,Q.jsxs)(`span`,{title:n,className:c(y,V,`gap-1.5`),children:[(0,Q.jsx)(`span`,{className:`tracking-[0.08em] uppercase`,children:e}),(0,Q.jsx)(`span`,{className:c(`font-mono tabular-nums text-foreground`,r&&`text-amber-800 dark:text-amber-400`),children:t})]})}function Ze({value:e,label:t}){return(0,Q.jsx)(`div`,{className:`opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100`,children:(0,Q.jsx)(W,{value:e,label:t})})}function Qe({label:e,value:t,hint:n,copy:r,copyLabel:i,warn:a=!1}){return(0,Q.jsxs)(`div`,{className:A,children:[(0,Q.jsx)(`dt`,{className:c(b,`w-[5.5rem] shrink-0`),children:e}),(0,Q.jsx)(`dd`,{className:c(`min-w-0 flex-1 truncate font-mono text-[11px]`,a&&`text-amber-800 dark:text-amber-400`),title:n,children:t}),r?(0,Q.jsx)(Ze,{value:r,label:i??`Copy ${e}`}):null]})}var $e=[`console.access.list`];function et(){return m({queryKey:$e,queryFn:async()=>{let e=await D();if(e.error||!e.data)throw Error(e.error?.message??`Empty access list`);return e.data}})}function tt({rows:e,selectedKey:t,query:n,loading:r=!1,onSelect:i}){let a=e.filter(e=>e.plane===`user`),o=e.filter(e=>e.plane===`operator`);return(0,Q.jsxs)(`div`,{id:`access-list`,"data-slot":`access-list`,className:`min-h-0 flex-1 overflow-y-auto`,children:[r&&e.length===0?(0,Q.jsx)(`p`,{className:te,children:`Loading…`}):null,!r&&e.length===0&&n.trim().length>0?(0,Q.jsx)(`p`,{className:te,children:`No keys match.`}):null,(0,Q.jsx)(nt,{label:`User`,icon:fe,rows:a,selectedKey:t,onSelect:i}),(0,Q.jsx)(nt,{label:`Operator`,icon:Ce,rows:o,selectedKey:t,onSelect:i})]})}function nt(e){return(0,Q.jsxs)(`section`,{className:L,"aria-label":e.label,children:[(0,Q.jsxs)(`div`,{className:j,children:[(0,Q.jsx)(r,{icon:e.icon,className:c(F,`text-muted-foreground`),"aria-hidden":!0}),(0,Q.jsx)(`span`,{className:c(z,`flex-1`),children:e.label}),(0,Q.jsx)(`span`,{className:N,children:e.rows.length})]}),e.rows.map(t=>{let n=t.id===e.selectedKey,i=t.revokedAt!==null;return(0,Q.jsxs)(`button`,{type:`button`,"data-slot":`access-key-row`,className:c(A,n&&`bg-muted/70 text-foreground`),onClick:()=>e.onSelect(t.id),children:[(0,Q.jsx)(`span`,{className:c(I,n&&`bg-sky-500`)}),(0,Q.jsx)(r,{icon:oe,className:c(F,`text-muted-foreground`)}),(0,Q.jsx)(`span`,{className:c(`min-w-0 truncate`,i&&`text-muted-foreground line-through`),children:t.name}),i?(0,Q.jsx)(`span`,{className:`ml-auto text-[10px] text-muted-foreground`,children:`revoked`}):t.unused90d?(0,Q.jsx)(`span`,{className:`ml-auto text-[10px] text-amber-700 dark:text-amber-400`,children:`unused`}):(0,Q.jsx)(`span`,{className:c(`ml-auto tabular-nums text-[10px] text-muted-foreground`),children:t.scopes.length})]},t.id)})]})}function rt({entries:e,rateMax:t,rateCount:n,rateUnit:i,onEntries:u,onRateMax:d,onRateCount:f,onRateUnit:p}){let[m,h]=(0,Y.useState)(``),_=Re(m),v=m.trim().length>0&&_.length===0,x=()=>{if(_.length===0)return;let t=new Set(e),n=[...e];for(let e of _)t.has(e)||(t.add(e),n.push(e));u(n),h(``)};return(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(`div`,{className:`border-b border-border/50`,"data-slot":`access-allow-field`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Allow`}),(0,Q.jsx)(a,{value:m,onChange:e=>h(e.target.value),onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),x())},placeholder:`203.0.113.4 or ci.example.com`,autoComplete:`off`,"aria-label":`Add allow entry`,"aria-invalid":v,flat:!0,className:c(J,`min-w-0 flex-1 font-mono`)}),e.length>0?(0,Q.jsx)(`span`,{className:c(N,`flex h-8 items-center pr-2`),children:e.length}):null,(0,Q.jsx)(`button`,{type:`button`,className:c(P,`h-8`),"aria-label":`Add allow entry`,disabled:_.length===0,onClick:x,children:(0,Q.jsx)(r,{icon:pe,className:`size-3.5`,"aria-hidden":!0})})]}),v?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-2 text-[11px] text-destructive`,children:`Need an IP or host — 203.0.113.4, ::1, localhost, or ci.example.com.`}):e.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-2 text-[11px] text-muted-foreground`,children:`Any client. Add an IP or host.`}):(0,Q.jsx)(`div`,{role:`list`,"aria-label":`Allow list`,children:e.map(t=>{let n=Le(t);return(0,Q.jsxs)(`div`,{role:`listitem`,"data-slot":`access-allow-entry`,"data-kind":n??`invalid`,className:c(A,`last:border-b-0`),children:[(0,Q.jsx)(`span`,{className:c(b,`w-[5.5rem] shrink-0 px-1.5`,n==null&&`text-destructive`),children:n===`ip`?`IP`:n===`host`?`Host`:`Bad`}),(0,Q.jsx)(`span`,{className:c(`min-w-0 truncate font-mono text-[11px]`,n==null&&`text-destructive`),children:t}),(0,Q.jsx)(`button`,{type:`button`,className:c(B,`ml-auto opacity-0 group-hover:opacity-100 focus-visible:opacity-100`),"aria-label":`Remove ${t}`,onClick:()=>u(e.filter(e=>e!==t)),children:(0,Q.jsx)(r,{icon:g,className:`size-3`,"aria-hidden":!0})})]},t)})})]}),(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-rate-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Rate`}),(0,Q.jsxs)(`label`,{className:`flex min-w-0 flex-1 items-stretch border-r border-border/50`,children:[(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pl-1 text-[10px] font-semibold tracking-[0.08em] text-muted-foreground uppercase`,children:`Max`}),(0,Q.jsx)(a,{value:t,onChange:e=>d(e.target.value),placeholder:`none`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate max`,flat:!0,className:c(J,`min-w-0 flex-1 px-2 font-mono tabular-nums`)})]}),(0,Q.jsxs)(`label`,{className:`flex min-w-0 flex-1 items-stretch`,children:[(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pl-2 text-[10px] font-semibold tracking-[0.08em] text-muted-foreground uppercase`,children:`Every`}),(0,Q.jsx)(a,{value:n,onChange:e=>f(e.target.value),placeholder:`1`,inputMode:`numeric`,autoComplete:`off`,"aria-label":`Rate window`,flat:!0,className:c(J,`min-w-0 flex-1 px-2 font-mono tabular-nums`)})]}),(0,Q.jsx)(`div`,{className:`flex h-8 shrink-0 items-stretch`,role:`group`,"aria-label":`Rate unit`,children:Ne.map(e=>(0,Q.jsxs)(o,{children:[(0,Q.jsx)(l,{render:t=>(0,Q.jsx)(`button`,{...t,type:`button`,"aria-pressed":i===e,"aria-label":Pe[e],onClick:n=>{t.onClick?.(n),p(e)},className:c(y,`h-8 px-1.5 font-mono font-semibold`,i===e?O:V),children:e})}),(0,Q.jsx)(s,{side:`bottom`,className:`text-[11px]`,children:Pe[e]})]},e))})]})]})}function it({expires:e,customExpires:t,onExpires:n,onCustomExpires:r}){return(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(ve,{label:`Expires`,children:[(0,Q.jsx)(K,{active:e===`never`,onClick:()=>n(`never`),children:`never`}),(0,Q.jsx)(K,{active:e===`30d`,onClick:()=>n(`30d`),children:`30d`}),(0,Q.jsx)(K,{active:e===`90d`,onClick:()=>n(`90d`),children:`90d`}),(0,Q.jsx)(K,{active:e===`custom`,onClick:()=>n(`custom`),children:`custom`})]}),e===`custom`?(0,Q.jsx)(_e,{label:`Duration`,hint:`7d · 12h · 30m`,children:(0,Q.jsx)(a,{value:t,onChange:e=>r(e.target.value),placeholder:`7d`,autoComplete:`off`,"aria-label":`Custom expiry duration`,flat:!0,className:c(J,`font-mono`)})}):null]})}function at({value:e,placeholder:t,label:n,onChange:i}){return(0,Q.jsxs)(`label`,{className:`relative min-w-0 flex-1`,children:[(0,Q.jsx)(r,{icon:he,className:`pointer-events-none absolute top-1/2 left-3 size-3.5 -translate-y-1/2 text-muted-foreground`,"aria-hidden":!0}),(0,Q.jsx)(a,{value:e,onChange:e=>i(e.target.value),placeholder:t,"aria-label":n,autoComplete:`off`,flat:!0,className:c(ye)})]})}function ot({scopes:e,selected:t,empty:n,onChange:r}){let[i,a]=(0,Y.useState)(``),o=new Set(t),s=(0,Y.useMemo)(()=>st(e,i),[e,i]),l=(0,Y.useMemo)(()=>He(s),[s]),u=e.join(`\0`);return(0,Y.useEffect)(()=>{a(``)},[u]),(0,Q.jsxs)(`div`,{className:`border-b border-border/50`,"data-slot":`access-scope-field`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Scopes`}),e.length>0?(0,Q.jsx)(at,{value:i,placeholder:`Search scopes`,label:`Search scopes`,onChange:a}):null]}),e.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:n}):s.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:`No scopes match.`}):(0,Q.jsx)(`div`,{className:`max-h-56 overflow-y-auto`,role:`group`,"aria-label":`Scopes`,children:l.map(e=>(0,Q.jsxs)(`div`,{className:`flex items-stretch border-t border-border/50`,role:`group`,"aria-label":e.group,children:[(0,Q.jsx)(`p`,{className:`flex w-[5.5rem] shrink-0 items-center px-4 font-mono text-[10px] font-semibold tracking-[0.08em] text-muted-foreground uppercase`,children:e.group}),(0,Q.jsx)(`div`,{className:`flex min-w-0 flex-1 flex-wrap items-stretch`,children:e.items.map(e=>{let n=o.has(e.scope);return(0,Q.jsx)(`button`,{type:`button`,"aria-pressed":n,"aria-label":e.scope,"data-slot":`access-scope-chip`,title:e.scope,onClick:()=>{r(n?t.filter(t=>t!==e.scope):[...t,e.scope])},className:c(`inline-flex h-8 min-w-0 items-center px-2 font-mono text-[10px] font-semibold outline-none select-none`,`focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-inset`,n?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`),children:e.action},e.scope)})})]},e.group))})]})}function st(e,t){let n=t.trim().toLowerCase();return n.length===0?e:e.filter(e=>e.toLowerCase().includes(n))}function ct({open:e,users:t,userScopes:n,operatorScopes:r,pending:i,error:o,onOpenChange:s,onSubmit:l}){let[m,g]=(0,Y.useState)(`user`),[v,y]=(0,Y.useState)(``),[b,x]=(0,Y.useState)(``),[S,C]=(0,Y.useState)([]),[w,ee]=(0,Y.useState)(`never`),[T,E]=(0,Y.useState)(``),[D,O]=(0,Y.useState)([]),[k,j]=(0,Y.useState)(``),[M,N]=(0,Y.useState)(``),[P,F]=(0,Y.useState)(`m`),[L,R]=(0,Y.useState)(``),z=t.find(e=>e.id===b)??null,te=(0,Y.useMemo)(()=>{let e=L.trim().toLowerCase(),n=e.length===0?t:t.filter(t=>t.name.toLowerCase().includes(e)||t.id.toLowerCase().includes(e)||t.email.toLowerCase().includes(e));return z&&!n.some(e=>e.id===z.id)?[z,...n]:n},[t,L,z]),B=(0,Y.useMemo)(()=>m===`operator`?r:z==null?[]:z.scopes.length>0?z.scopes:n,[m,n,r,z]);(0,Y.useEffect)(()=>{e||(g(`user`),y(``),x(``),C([]),ee(`never`),E(``),O([]),j(``),N(``),F(`m`),R(``))},[e]),(0,Y.useEffect)(()=>{C(e=>{let t=e.filter(e=>B.includes(e));if(t.length>0)return t;let n=B[0];return n?[n]:[]})},[B]);let V=De(T),H=Fe(k,M,P),ne=v.trim().length>0&&S.length>0&&!i&&H!==void 0&&(m===`operator`||b.length>0)&&(w!==`custom`||V>0);return(0,Q.jsx)(d,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:s,children:(0,Q.jsxs)(p,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-create-sheet`,children:[(0,Q.jsxs)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:`Create API key`}),(0,Q.jsx)(h,{className:`text-[11px]`,children:m===`user`?`User-plane keys require an issuer. The key cannot exceed that user's grants.`:`Operator keys inherit the actor ceiling. Absence is impossible — only grantable scopes appear.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-name-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Name`}),(0,Q.jsx)(a,{value:v,onChange:e=>y(e.target.value),placeholder:`CI deploy`,autoComplete:`off`,"aria-label":`Key name`,flat:!0,className:c(J,`min-w-0 flex-1`)})]}),(0,Q.jsxs)(ve,{label:`Plane`,children:[(0,Q.jsx)(K,{active:m===`user`,icon:fe,onClick:()=>{g(`user`)},children:`user`}),(0,Q.jsx)(K,{active:m===`operator`,icon:Ce,onClick:()=>{g(`operator`),x(``),R(``)},children:`operator`})]}),(0,Q.jsx)(it,{expires:w,customExpires:T,onExpires:ee,onCustomExpires:E}),(0,Q.jsx)(rt,{entries:D,rateMax:k,rateCount:M,rateUnit:P,onEntries:O,onRateMax:j,onRateCount:N,onRateUnit:F}),m===`user`?(0,Q.jsxs)(`div`,{className:`border-b border-border/50`,"data-slot":`access-issuer-field`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Issuer`}),t.length>0?(0,Q.jsx)(at,{value:L,placeholder:`Search users`,label:`Search users`,onChange:R}):null]}),t.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:`No users on this plane.`}):te.length===0?(0,Q.jsx)(`p`,{className:`border-t border-border/50 px-4 py-3 text-[11px] text-muted-foreground`,children:`No users match.`}):(0,Q.jsx)(`div`,{className:`max-h-56 overflow-y-auto`,children:te.map(e=>{let t=e.id===b;return(0,Q.jsxs)(`button`,{type:`button`,"data-slot":`access-creator-user`,"aria-pressed":t,className:c(A,t&&`bg-muted/70 text-foreground`),onClick:()=>x(e.id),children:[(0,Q.jsx)(`span`,{className:c(I,t&&`bg-sky-500`)}),(0,Q.jsx)(`span`,{className:`min-w-0 truncate`,children:e.name}),(0,Q.jsx)(`span`,{className:`ml-auto shrink-0 font-mono text-[10px] text-muted-foreground`,children:e.id})]},e.id)})})]}):null,(0,Q.jsx)(ot,{scopes:B,selected:S,empty:m===`user`&&b.length===0?`Select an issuer to see grantable scopes.`:m===`user`?`This user has no grants.`:`No grantable scopes on this plane.`,onChange:C}),o?(0,Q.jsx)(be,{slot:`access-create-error`,children:o}):null]}),(0,Q.jsxs)(u,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>s(!1),disabled:i,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!ne,"data-slot":`access-create-submit`,onClick:()=>{if(H===void 0)return;let e=Oe(w,Date.now(),T);l({plane:m,name:v.trim(),scopes:S,...m===`user`?{creatorUserId:b}:{},...e==null?{}:{expiresAt:e},...H==null?{}:{rateLimit:H},...D.length>0?{ipAllowlist:D}:{}})},children:i?`Creating…`:`Create`})]})]})})}function lt({open:e,keyRow:t,users:n,operators:i,operatorScopes:o,pending:s,error:l,now:m=Date.now(),onOpenChange:g,onSubmit:v}){let y=ke(t.expiresAt,m),[b,x]=(0,Y.useState)(t.name),[S,C]=(0,Y.useState)(t.scopes),[w,ee]=(0,Y.useState)(y.choice),[T,E]=(0,Y.useState)(y.custom),[D,O]=(0,Y.useState)(t.ipAllowlist),k=Ie(t.rateLimit?.per??``),[A,j]=(0,Y.useState)(t.rateLimit?String(t.rateLimit.max):``),[M,N]=(0,Y.useState)(k.count),[P,F]=(0,Y.useState)(k.unit);(0,Y.useEffect)(()=>{if(!e)return;let n=ke(t.expiresAt,m);x(t.name),C(t.scopes),ee(n.choice),E(n.custom),O(t.ipAllowlist);let r=Ie(t.rateLimit?.per??``);j(t.rateLimit?String(t.rateLimit.max):``),N(r.count),F(r.unit)},[e,t.id]);let I=t.creatorId??``,L=(0,Y.useMemo)(()=>I.length===0?null:n.find(e=>e.id===I)??i.find(e=>e.id===I)??null,[I,i,n]),R=L?L.name.trim()||L.email.trim()||L.id:I||`Unknown`,z=(0,Y.useMemo)(()=>{let e=t.creatorScopes??[];return e.length>0?e:t.plane===`operator`?L&&L.scopes.length>0?L.scopes:o:L?.scopes??[]},[L,t.creatorScopes,t.plane,o]),te=Array.from(new Set([...z,...t.scopes])),B=De(T),V=Fe(A,M,P),H=b.trim().length>0&&S.length>0&&!s&&V!==void 0&&(w!==`custom`||B>0);return(0,Q.jsx)(d,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:g,children:(0,Q.jsxs)(p,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-edit-sheet`,children:[(0,Q.jsxs)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:`Edit key`}),(0,Q.jsx)(h,{className:`text-[11px]`,children:`Change name, expiry, allow, rate, and scopes. Issuer stays frozen.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-name-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Name`}),(0,Q.jsx)(a,{value:b,onChange:e=>x(e.target.value),placeholder:`CI deploy`,autoComplete:`off`,"aria-label":`Key name`,flat:!0,className:c(J,`min-w-0 flex-1`)})]}),(0,Q.jsxs)(`div`,{className:`flex items-stretch border-b border-border/50`,"data-slot":`access-issuer-field`,children:[(0,Q.jsx)(`p`,{className:`flex h-8 w-[5.5rem] shrink-0 items-center px-4 text-[10px] font-semibold tracking-[0.12em] text-muted-foreground uppercase`,children:`Issuer`}),(0,Q.jsxs)(`p`,{className:`flex min-w-0 flex-1 items-center gap-1.5 px-4 text-[12px] text-muted-foreground`,"aria-label":`Issuer ${R}`,children:[(0,Q.jsx)(r,{icon:t.plane===`operator`?Ce:fe,className:`size-3 shrink-0`,"aria-hidden":!0}),(0,Q.jsx)(`span`,{className:`min-w-0 truncate text-foreground`,children:R})]}),I.length>0&&I!==R?(0,Q.jsx)(`span`,{className:`flex h-8 shrink-0 items-center pr-4 font-mono text-[10px] text-muted-foreground`,children:I}):null]}),(0,Q.jsx)(it,{expires:w,customExpires:T,onExpires:ee,onCustomExpires:E}),(0,Q.jsx)(rt,{entries:D,rateMax:A,rateCount:M,rateUnit:P,onEntries:O,onRateMax:j,onRateCount:N,onRateUnit:F}),(0,Q.jsx)(ot,{scopes:te,selected:S,empty:t.plane===`user`&&L==null?`Issuer is gone. Only scopes already on this key remain.`:t.plane===`user`?`This issuer has no grants.`:`No grantable scopes on this plane.`,onChange:C}),l?(0,Q.jsx)(be,{slot:`access-edit-error`,children:l}):null]}),(0,Q.jsxs)(u,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>g(!1),disabled:s,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!H,"data-slot":`access-edit-submit`,onClick:()=>{V!==void 0&&v({name:b.trim(),scopes:S,expiresAt:Oe(w,Date.now(),T),rateLimit:V,ipAllowlist:D})},children:s?`Saving…`:`Save`})]})]})})}function ut({open:e,keyRow:t,pending:n,error:r,now:i=Date.now(),onOpenChange:a,onSubmit:o}){let s=je(t.expiresAt,i),[c,l]=(0,Y.useState)(s.choice),[m,g]=(0,Y.useState)(s.custom);(0,Y.useEffect)(()=>{if(!e)return;let n=je(t.expiresAt,i);l(n.choice),g(n.custom)},[e,t.id]);let v=De(m);return(0,Q.jsx)(d,{open:e,modal:!1,disablePointerDismissal:!0,onOpenChange:a,children:(0,Q.jsxs)(p,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-refresh-sheet`,children:[(0,Q.jsxs)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:`Refresh expiry`}),(0,Q.jsx)(h,{className:`text-[11px]`,children:`Reset the deadline from now. The secret does not change.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsx)(it,{expires:c,customExpires:m,onExpires:l,onCustomExpires:g}),r?(0,Q.jsx)(be,{slot:`access-refresh-error`,children:r}):null]}),(0,Q.jsxs)(u,{children:[(0,Q.jsx)(q,{split:!0,onClick:()=>a(!1),disabled:n,children:`Cancel`}),(0,Q.jsx)(q,{variant:`default`,disabled:!(!n&&(c!==`custom`||v>0)),"data-slot":`access-refresh-submit`,onClick:()=>{o(Oe(c,Date.now(),m))},children:n?`Refreshing…`:`Refresh`})]})]})})}function dt({secret:e,title:t=`New API key`,onDismiss:n}){let[r,i]=(0,Y.useState)(!1);return(0,Q.jsx)(d,{open:e!==null,onOpenChange:e=>{e||n()},children:(0,Q.jsxs)(p,{side:`right`,showOverlay:!1,className:`gap-0 p-0 data-[side=right]:sm:max-w-md`,"data-slot":`access-secret-sheet`,children:[(0,Q.jsxs)(f,{className:`gap-1 border-b border-border/50`,children:[(0,Q.jsx)(_,{className:`text-sm`,children:t}),(0,Q.jsx)(h,{className:`text-[11px]`,children:`Shown once. Store it offline. This Console process does not keep it for you.`})]}),(0,Q.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,Q.jsx)(`p`,{className:`border-b border-border/50 px-4 py-3 font-mono text-[11px] break-all`,children:e}),(0,Q.jsx)(`p`,{className:`px-4 py-3 text-[11px] text-muted-foreground`,role:`status`,children:`Copying puts the secret on the clipboard. Anyone with this device can read it until you clear it.`})]}),(0,Q.jsxs)(u,{children:[(0,Q.jsx)(q,{split:!0,disabled:!e,onClick:()=>{e&&(navigator.clipboard?.writeText(e),i(!0))},children:r?`Copied`:`Copy (clipboard warning)`}),(0,Q.jsx)(q,{variant:`destructive`,onClick:()=>{i(!1),n()},children:`I have stored this key`})]})]})})}function ft(){let e=et(),t=i(),{query:n,selectedKey:a,action:o,setQuery:s,setSelectedKey:c,setAction:l}=me(),u=ce(),[d]=(0,Y.useState)(()=>Date.now()),[f,p]=(0,Y.useState)(null),[h,g]=(0,Y.useState)(`New API key`),_=(0,Y.useMemo)(()=>{let t=e.data?.userPlane.keys??[],n=e.data?.operatorPlane.keys??[];return[...t,...n]},[e.data]),v=(0,Y.useMemo)(()=>{let e=n.trim().toLowerCase();return e?_.filter(t=>t.name.toLowerCase().includes(e)||t.id.toLowerCase().includes(e)||t.scopes.some(t=>t.toLowerCase().includes(e))):_},[_,n]),y=v.find(e=>e.id===a)??_.find(e=>e.id===a),b=m({queryKey:[`console.access.blast`,y?.id],queryFn:async()=>{if(!y)throw Error(`no key`);let e=await T(y.id);if(e.error||!e.data)throw Error(e.error?.message??`blast failed`);return e.data},enabled:y!==void 0});(0,Y.useEffect)(()=>{let e=e=>{if(e.key!==`/`||e.metaKey||e.ctrlKey||e.altKey)return;let t=e.target;if(t instanceof HTMLElement){let e=t.tagName;if(e===`INPUT`||e===`TEXTAREA`||t.isContentEditable)return}e.preventDefault(),document.querySelector(`[data-slot='access-search-input']`)?.focus()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let E=()=>{t.invalidateQueries({queryKey:$e})},D=G({mutationFn:ee,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`create failed`);g(`New API key`),p(e.data.secret),c(e.data.key.id),l(null),E()}}),O=G({mutationFn:e=>C({keyId:y.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`rotate failed`);g(`Rotated API key`),p(e.data.secret),l(null),E()}}),A=G({mutationFn:e=>S({keyId:y.id,confirmation:e.confirmation,reason:e.reason}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`revoke failed`);l(null),E()}}),j=G({mutationFn:w,onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`update failed`);l(null),E()}}),N=G({mutationFn:e=>w({keyId:y.id,expiresAt:e}),onSuccess:e=>{if(e.error||!e.data)throw Error(e.error?k(e.error):`refresh failed`);l(null),E()}}),F=(0,Q.jsx)(le,{open:u.open,onToggle:u.toggle,noun:`keys`,controlsId:`access-list`,dataSlot:`access-list-toggle`});return(0,Q.jsxs)(`div`,{className:R,"data-slot":`access-page`,children:[(0,Q.jsxs)(ie,{orientation:`horizontal`,className:`min-h-0 flex-1`,children:[(0,Q.jsx)(se,{panelRef:u.panelRef,collapsible:!0,collapsedSize:0,defaultSize:M.start.defaultSize,minSize:M.start.minSize,onResize:u.onResize,className:`min-h-0 overflow-hidden`,children:(0,Q.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col overflow-hidden`,children:[(0,Q.jsxs)(`div`,{className:x,children:[(0,Q.jsx)(ae,{"data-slot":`access-search-input`,placeholder:`Search keys`,"aria-label":`Search keys`,value:n,onChange:e=>s(e.target.value)}),(0,Q.jsx)(U,{label:`Create key`,className:`flex self-stretch`,children:(0,Q.jsx)(`button`,{type:`button`,className:P,"data-slot":`access-create`,"aria-label":`Create key`,onClick:()=>l(`create`),children:(0,Q.jsx)(r,{icon:pe,className:`size-3.5`})})})]}),(0,Q.jsx)(tt,{rows:v,selectedKey:a,query:n,loading:e.isLoading,onSelect:c})]})}),u.open?(0,Q.jsx)(ue,{withHandle:!0}):null,(0,Q.jsx)(se,{defaultSize:M.end.defaultSize,minSize:M.end.minSize,className:`min-h-0 overflow-hidden`,children:y?(0,Q.jsx)(Ge,{keyRow:y,blast:b.data,now:d,leading:F,onEdit:()=>l(`edit`),onRefresh:()=>l(`refresh`),onRevoke:()=>l(`revoke`),onRotate:()=>l(`rotate`),rotatePending:O.isPending}):(0,Q.jsx)(ge,{icon:de.gate.icon,iconClassName:`border-border/70`,title:e.isLoading?`Loading keys…`:_.length>0?`Select a key`:`No keys yet`,leading:F,description:e.isLoading?`Reading planes and grantable scopes.`:_.length>0&&n.trim().length>0?(0,Q.jsxs)(Q.Fragment,{children:[`Nothing matches `,(0,Q.jsx)(`span`,{className:`font-mono`,children:n}),`.`,` `,(0,Q.jsx)(`button`,{type:`button`,className:`underline underline-offset-2 hover:text-foreground`,onClick:()=>s(``),children:`Clear search`})]}):`Create one to mint a secret once.`})})]}),(0,Q.jsx)(ct,{open:o===`create`,users:e.data?.userPlane.users??[],userScopes:e.data?.userPlane.grantableScopes??[],operatorScopes:e.data?.operatorPlane.grantableScopes??[],pending:D.isPending,error:D.error instanceof Error?D.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>D.mutate(e)}),y?(0,Q.jsx)(lt,{open:o===`edit`,keyRow:y,users:e.data?.userPlane.users??[],operators:e.data?.operatorPlane.operators??[],operatorScopes:e.data?.operatorPlane.grantableScopes??[],pending:j.isPending,error:j.error instanceof Error?j.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>j.mutate({keyId:y.id,...e})}):null,y?(0,Q.jsx)(ut,{open:o===`refresh`,keyRow:y,pending:N.isPending,error:N.error instanceof Error?N.error.message:null,onOpenChange:e=>{e||l(null)},onSubmit:e=>N.mutate(e)}):null,y?(0,Q.jsx)(Se,{open:o===`revoke`,onOpenChange:e=>{e||l(null)},phrase:`REVOKE`,title:`Revoke ${y.name}`,description:`Irreversible. Residual sessions may continue for the access TTL.`,pending:A.isPending,error:A.error instanceof Error?A.error.message:null,confirmLabel:`Revoke key`,slot:`access-revoke-sheet`,onConfirm:e=>A.mutate({confirmation:`REVOKE`,reason:e.reason})}):null,y?(0,Q.jsx)(Se,{open:o===`rotate`,onOpenChange:e=>{e||l(null)},phrase:`ROTATE`,title:`Rotate ${y.name}`,description:`Mints a new secret once. The current secret stops after residual TTL.`,pending:O.isPending,error:O.error instanceof Error?O.error.message:null,confirmLabel:`Rotate key`,confirmVariant:`ghost`,slot:`access-rotate-sheet`,onConfirm:e=>O.mutate({confirmation:`ROTATE`,reason:e.reason})}):null,(0,Q.jsx)(dt,{secret:f,title:h,onDismiss:()=>p(null)})]})}export{ft as AccessPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{A as e}from"./xyflow-yApv7D4e.js";import{Qn as t}from"./shortcut-keys-DTE1sjTC.js";import{c as n,l as r,st as i}from"./index-
|
|
1
|
+
import{A as e}from"./xyflow-yApv7D4e.js";import{Qn as t}from"./shortcut-keys-DTE1sjTC.js";import{c as n,l as r,st as i}from"./index-CmRFeWdT.js";var a=e();function o({open:e,openHeight:o=`auto`,className:s,style:c,transition:l,...u}){let d=r()??!1;return(0,a.jsx)(n.div,{...u,"aria-hidden":!e,inert:!e||void 0,initial:!1,animate:d?{opacity:+!!e}:{opacity:+!!e,clipPath:e?`inset(0 0 0% 0)`:`inset(0 0 100% 0)`,y:e?0:-4},transition:l??{duration:d?0:e?.22:.14,ease:i},className:t(`overflow-hidden`,s),style:{...c,height:e?o:0,pointerEvents:e?void 0:`none`,transformOrigin:`top`}})}export{o as t};
|