@stigmer/server 3.15.3-dev.20260914100347 → 3.15.3-dev.20260914121148
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/dist/extensions/list-read-scope.d.ts +41 -4
- package/dist/extensions/list-read-scope.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.js +44 -7
- package/dist/extensions/list-read-scope.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +21 -0
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +28 -0
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +7 -26
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/shapes.d.ts +18 -0
- package/dist/pipeline/steps/shapes.d.ts.map +1 -1
- package/dist/pipeline/steps/shapes.js +29 -0
- package/dist/pipeline/steps/shapes.js.map +1 -1
- package/package.json +4 -4
- package/src/extensions/__tests__/list-read-scope.test.ts +138 -0
- package/src/extensions/list-read-scope.ts +87 -9
- package/src/pipeline/__tests__/inherited-authorization-parent.test.ts +51 -0
- package/src/pipeline/apiresource-meta.ts +37 -0
- package/src/pipeline/steps/authorization-tuples.ts +7 -28
- package/src/pipeline/steps/shapes.ts +31 -0
|
@@ -71,19 +71,49 @@
|
|
|
71
71
|
* untouched with the org unconsulted (the first line above). Two lanes
|
|
72
72
|
* have no org on the wire (`session.list`, `apiKey.findAll`) and offer
|
|
73
73
|
* the kind until P1 entry 12 pages them.
|
|
74
|
+
* - A KIND WHOSE AUTHORIZATION IS ITS PARENT'S CARRIES THAT PARENT ON
|
|
75
|
+
* EVERY CANDIDATE (stigmer-cloud 20260913.04 T04, Q-LB-33..36). When
|
|
76
|
+
* `kind_meta` declares PARENT scope with INHERITED owner
|
|
77
|
+
* (`inheritedAuthorizationParentOf`; today agent_execution → session),
|
|
78
|
+
* the helper fills `ListEntryMeta.authorizationParent` from the row's
|
|
79
|
+
* spec through the same read the tuple lifecycle wrote the parent link
|
|
80
|
+
* with. A driver MAY answer the parent's question in place of the
|
|
81
|
+
* child's: by the model's construction the two answers are identical
|
|
82
|
+
* (the child's `owner`, `viewer` and `can_view` are all `from` the
|
|
83
|
+
* parent), and the parent's resolution is direct tuples where the
|
|
84
|
+
* child's walks the parent's whole set (OpenFGA's learned `weight2`
|
|
85
|
+
* strategy read the founder's 423 sessions per execution; that is what
|
|
86
|
+
* rolled back twice on 2026-09-14). A driver that ignores the field is
|
|
87
|
+
* still correct. The one observable difference is stated, not hidden:
|
|
88
|
+
* a row whose parent tuple never landed (the create chain's "row
|
|
89
|
+
* survives, tuple write failed" state, healed by retry) is hidden by
|
|
90
|
+
* the child's check and shown by the parent's — the row's truth, read
|
|
91
|
+
* from the field the tuple was derived from, and no widening: nobody
|
|
92
|
+
* reaches a parent through it who could not create the row in it.
|
|
74
93
|
*/
|
|
75
94
|
import type { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
76
95
|
import type { CallerIdentity } from "./identity.js";
|
|
96
|
+
import type { ResolvedParentLink } from "./resource-authorization.js";
|
|
77
97
|
/**
|
|
78
98
|
* The candidate metadata a post-scan lane offers the scope — the three
|
|
79
99
|
* fields the cloud driver's rules key on (id for the FGA intersection,
|
|
80
100
|
* labels for the guest cookie rule; org rides along for symmetry with
|
|
81
|
-
* the consumer-owned intersection, so a driver can log or assert on it)
|
|
101
|
+
* the consumer-owned intersection, so a driver can log or assert on it),
|
|
102
|
+
* plus the parent for kinds whose authorization is their parent's.
|
|
82
103
|
*/
|
|
83
104
|
export interface ListEntryMeta {
|
|
84
105
|
readonly id: string;
|
|
85
106
|
readonly org: string;
|
|
86
107
|
readonly labels: Readonly<Record<string, string>>;
|
|
108
|
+
/**
|
|
109
|
+
* OPTIONAL (added 20260913.04 T04): the parent this row's authorization
|
|
110
|
+
* IS — present only for a kind whose `kind_meta` is PARENT scope with
|
|
111
|
+
* INHERITED owner (the header's last contract line), the same
|
|
112
|
+
* `ResolvedParentLink` the tuple lifecycle wrote for the row. Absent for
|
|
113
|
+
* every other kind, and absent when the row's spec does not name its
|
|
114
|
+
* parent — a driver then asks about the row itself, never drops it.
|
|
115
|
+
*/
|
|
116
|
+
readonly authorizationParent?: ResolvedParentLink;
|
|
87
117
|
}
|
|
88
118
|
/** The scope contract (single-instance point, ExtensionDrivers.listReadScope). */
|
|
89
119
|
export interface ListReadScope {
|
|
@@ -105,7 +135,10 @@ export interface ListReadScope {
|
|
|
105
135
|
}
|
|
106
136
|
/**
|
|
107
137
|
* The shape every stored API resource message presents to the shared
|
|
108
|
-
* helper — the metadata fields the scope's candidates are built from
|
|
138
|
+
* helper — the metadata fields the scope's candidates are built from, and
|
|
139
|
+
* the spec the parent is read from (structurally, by `kind_meta`'s
|
|
140
|
+
* `spec_field`; typed loosely because the field differs per kind and the
|
|
141
|
+
* read is `parentIdOf`'s).
|
|
109
142
|
*/
|
|
110
143
|
export interface ScopedListResource {
|
|
111
144
|
metadata?: {
|
|
@@ -113,6 +146,7 @@ export interface ScopedListResource {
|
|
|
113
146
|
org?: string;
|
|
114
147
|
labels?: Record<string, string>;
|
|
115
148
|
};
|
|
149
|
+
spec?: object;
|
|
116
150
|
}
|
|
117
151
|
/**
|
|
118
152
|
* The ONE consumption idiom for post-scan lanes (the shared-step
|
|
@@ -128,8 +162,11 @@ export interface ScopedListResource {
|
|
|
128
162
|
* permission-bounded across orgs, verified per lane in the entry's
|
|
129
163
|
* census — lanes Java does not org-narrow pass ""), then to the kept
|
|
130
164
|
* ids. The same set either way (both are per-row predicates); the
|
|
131
|
-
* order is the header's last contract line: the scope sees
|
|
132
|
-
* rows, not the kind's
|
|
165
|
+
* order is the header's second-to-last contract line: the scope sees
|
|
166
|
+
* the org's rows, not the kind's;
|
|
167
|
+
* - the candidates of a kind whose authorization is its parent's carry
|
|
168
|
+
* `authorizationParent` (the header's last line), resolved once per
|
|
169
|
+
* call from `kind_meta` and once per row from the spec.
|
|
133
170
|
*/
|
|
134
171
|
export declare function restrictListByReadScope<T extends ScopedListResource>(scope: ListReadScope | undefined, caller: CallerIdentity, kind: ApiResourceKind, resources: ReadonlyArray<T>, requestOrg: string): Promise<T[]>;
|
|
135
172
|
//# sourceMappingURL=list-read-scope.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-read-scope.d.ts","sourceRoot":"","sources":["../../src/extensions/list-read-scope.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"list-read-scope.d.ts","sourceRoot":"","sources":["../../src/extensions/list-read-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qFAAqF,CAAC;AAY3H,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD;;;;;;;OAOG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,CAAC;CACnD;AAED,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,qBAAqB,CACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAEhC;;;;;OAKG;IACH,mBAAmB,CACjB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,GACpC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE;QACT,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,uBAAuB,CAAC,CAAC,SAAS,kBAAkB,EACxE,KAAK,EAAE,aAAa,GAAG,SAAS,EAChC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,eAAe,EACrB,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAC3B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,EAAE,CAAC,CAsBd"}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// The first VALUE imports from extensions/ into pipeline/ (every other
|
|
2
|
+
// crossing is a type): two pure leaf modules that import nothing but
|
|
3
|
+
// protos, so there is no cycle. Deliberate — one resolver for the tuple
|
|
4
|
+
// writer and the list reader beats a copy in the seam.
|
|
5
|
+
import { getKindEnum, inheritedAuthorizationParentOf, } from "../pipeline/apiresource-meta.js";
|
|
6
|
+
import { parentIdOf } from "../pipeline/steps/shapes.js";
|
|
1
7
|
/**
|
|
2
8
|
* The ONE consumption idiom for post-scan lanes (the shared-step
|
|
3
9
|
* discipline of pipeline/steps/authorization-tuples.ts, rendered as a
|
|
@@ -12,8 +18,11 @@
|
|
|
12
18
|
* permission-bounded across orgs, verified per lane in the entry's
|
|
13
19
|
* census — lanes Java does not org-narrow pass ""), then to the kept
|
|
14
20
|
* ids. The same set either way (both are per-row predicates); the
|
|
15
|
-
* order is the header's last contract line: the scope sees
|
|
16
|
-
* rows, not the kind's
|
|
21
|
+
* order is the header's second-to-last contract line: the scope sees
|
|
22
|
+
* the org's rows, not the kind's;
|
|
23
|
+
* - the candidates of a kind whose authorization is its parent's carry
|
|
24
|
+
* `authorizationParent` (the header's last line), resolved once per
|
|
25
|
+
* call from `kind_meta` and once per row from the spec.
|
|
17
26
|
*/
|
|
18
27
|
export async function restrictListByReadScope(scope, caller, kind, resources, requestOrg) {
|
|
19
28
|
if (scope === undefined) {
|
|
@@ -22,12 +31,40 @@ export async function restrictListByReadScope(scope, caller, kind, resources, re
|
|
|
22
31
|
const offered = requestOrg === ""
|
|
23
32
|
? resources
|
|
24
33
|
: resources.filter((resource) => resource.metadata?.org === requestOrg);
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
const parentOf = authorizationParentReader(kind);
|
|
35
|
+
const entries = offered.map((resource) => {
|
|
36
|
+
const entry = {
|
|
37
|
+
id: resource.metadata?.id ?? "",
|
|
38
|
+
org: resource.metadata?.org ?? "",
|
|
39
|
+
labels: resource.metadata?.labels ?? {},
|
|
40
|
+
};
|
|
41
|
+
const authorizationParent = parentOf(resource);
|
|
42
|
+
return authorizationParent === undefined
|
|
43
|
+
? entry
|
|
44
|
+
: { ...entry, authorizationParent };
|
|
45
|
+
});
|
|
30
46
|
const keptIds = await scope.restrictListEntries(caller, kind, entries);
|
|
31
47
|
return offered.filter((resource) => keptIds.has(resource.metadata?.id ?? ""));
|
|
32
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* The per-row parent reader for `kind`: a constant `undefined` for the 27
|
|
51
|
+
* kinds whose authorization is their own, and for the kind whose
|
|
52
|
+
* authorization is its parent's, the `ResolvedParentLink` the tuple
|
|
53
|
+
* lifecycle wrote — or undefined for a row whose spec does not name it
|
|
54
|
+
* (never a throw: a list is not a create). `kind_meta` is consulted once
|
|
55
|
+
* per call, not once per row.
|
|
56
|
+
*/
|
|
57
|
+
function authorizationParentReader(kind) {
|
|
58
|
+
const parent = inheritedAuthorizationParentOf(kind);
|
|
59
|
+
if (parent === undefined) {
|
|
60
|
+
return () => undefined;
|
|
61
|
+
}
|
|
62
|
+
const parentKind = getKindEnum(parent.kind);
|
|
63
|
+
return (resource) => {
|
|
64
|
+
const parentId = parentIdOf(resource, parent.specField);
|
|
65
|
+
return parentId === ""
|
|
66
|
+
? undefined
|
|
67
|
+
: { relation: parent.relation, parentKind, parentId };
|
|
68
|
+
};
|
|
69
|
+
}
|
|
33
70
|
//# sourceMappingURL=list-read-scope.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-read-scope.js","sourceRoot":"","sources":["../../src/extensions/list-read-scope.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"list-read-scope.js","sourceRoot":"","sources":["../../src/extensions/list-read-scope.ts"],"names":[],"mappings":"AA+FA,uEAAuE;AACvE,qEAAqE;AACrE,wEAAwE;AACxE,uDAAuD;AACvD,OAAO,EACL,WAAW,EACX,8BAA8B,GAC/B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAsEzD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAgC,EAChC,MAAsB,EACtB,IAAqB,EACrB,SAA2B,EAC3B,UAAkB;IAElB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IACxB,CAAC;IACD,MAAM,OAAO,GACX,UAAU,KAAK,EAAE;QACf,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,OAAO,GAAoB,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;QACxD,MAAM,KAAK,GAAkB;YAC3B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE;YAC/B,GAAG,EAAE,QAAQ,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE;YACjC,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE;SACxC,CAAC;QACF,MAAM,mBAAmB,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC/C,OAAO,mBAAmB,KAAK,SAAS;YACtC,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,mBAAmB,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,yBAAyB,CAChC,IAAqB;IAErB,MAAM,MAAM,GAAG,8BAA8B,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC;IACzB,CAAC;IACD,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5C,OAAO,CAAC,QAAQ,EAAE,EAAE;QAClB,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QACxD,OAAO,QAAQ,KAAK,EAAE;YACpB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAC1D,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
2
2
|
import { ApiResourceKind, ResourceTier } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
3
3
|
import type { ApiResourceKindMeta } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
4
|
+
import type { ParentRelationConfig } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/authorization_config_pb";
|
|
4
5
|
import type { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
5
6
|
import { ServerEdition } from "@stigmer/protos/ai/stigmer/platform/v1/server_info_pb";
|
|
6
7
|
/** Go GetKindMeta: the kind_meta extension of the enum value. */
|
|
7
8
|
export declare function getKindMeta(kind: ApiResourceKind): ApiResourceKindMeta;
|
|
9
|
+
/**
|
|
10
|
+
* The parent a kind's authorization IS — the `kind_meta` declaration that
|
|
11
|
+
* a kind's permissions are its parent's whole, not its own: PARENT scope
|
|
12
|
+
* (the one structural link is to that parent) AND INHERITED owner (no
|
|
13
|
+
* owner tuple of its own; `owner from <parent>` in the FGA model).
|
|
14
|
+
* Undefined for every other kind, including kinds with additional parents
|
|
15
|
+
* whose inheritance is partial (a workflow_execution's opt-in
|
|
16
|
+
* `execution_viewer from workflow_instance` sits beside its own owner).
|
|
17
|
+
*
|
|
18
|
+
* Today exactly one kind answers: agent_execution → session (its
|
|
19
|
+
* `can_view` is `viewer or can_view from session`, and `viewer` is the
|
|
20
|
+
* session's). The list read scope reads this to carry the parent on every
|
|
21
|
+
* candidate (`ListEntryMeta.authorizationParent`, 20260913.04 T04), so a
|
|
22
|
+
* composed driver may ask its authorization backend about the parent —
|
|
23
|
+
* direct tuples — instead of the child, whose resolution walks the
|
|
24
|
+
* parent's whole set. A second kind declaring the pair gets the behavior
|
|
25
|
+
* from this declaration alone; the table pin in __tests__ makes that a
|
|
26
|
+
* reviewed change.
|
|
27
|
+
*/
|
|
28
|
+
export declare function inheritedAuthorizationParentOf(kind: ApiResourceKind): ParentRelationConfig | undefined;
|
|
8
29
|
/** Go GetIdPrefix: e.g. agent → "agt". */
|
|
9
30
|
export declare function getIdPrefix(kind: ApiResourceKind): string;
|
|
10
31
|
/** Go GetKindName: the canonical name, e.g. agent → "Agent" (error copy uses this). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiresource-meta.d.ts","sourceRoot":"","sources":["../../src/pipeline/apiresource-meta.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAC/F,OAAO,EACL,eAAe,EAEf,YAAY,EAEb,MAAM,qFAAqF,CAAC;AAC7F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qFAAqF,CAAC;
|
|
1
|
+
{"version":3,"file":"apiresource-meta.d.ts","sourceRoot":"","sources":["../../src/pipeline/apiresource-meta.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAC/F,OAAO,EACL,eAAe,EAEf,YAAY,EAEb,MAAM,qFAAqF,CAAC;AAC7F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qFAAqF,CAAC;AAK/H,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wFAAwF,CAAC;AACnI,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2CAA2C,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,uDAAuD,CAAC;AAEtF,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,mBAAmB,CAMtE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,eAAe,GACpB,oBAAoB,GAAG,SAAS,CASlC;AAED,0CAA0C;AAC1C,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAEzD;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAS9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAI7D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAO1D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,eAAe,GACpB,qBAAqB,CAKvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,GACpB,aAAa,CAAC,OAAO,CAAC,CAMxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,aAAa,GACrB,OAAO,CAKT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAET;AAkCD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,eAAe,EACrB,UAAU,EAAE,qBAAqB,GAChC,OAAO,CAYT;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAavE"}
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
import { getOption, hasOption } from "@bufbuild/protobuf";
|
|
46
46
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
47
47
|
import { ApiResourceKind, ApiResourceKindSchema, ResourceTier, kind_meta, } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
48
|
+
import { AuthorizationScopeType, OwnerAttributionType, } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/authorization_config_pb";
|
|
48
49
|
import { ServerEdition } from "@stigmer/protos/ai/stigmer/platform/v1/server_info_pb";
|
|
49
50
|
/** Go GetKindMeta: the kind_meta extension of the enum value. */
|
|
50
51
|
export function getKindMeta(kind) {
|
|
@@ -54,6 +55,33 @@ export function getKindMeta(kind) {
|
|
|
54
55
|
}
|
|
55
56
|
return getOption(valueDesc, kind_meta);
|
|
56
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* The parent a kind's authorization IS — the `kind_meta` declaration that
|
|
60
|
+
* a kind's permissions are its parent's whole, not its own: PARENT scope
|
|
61
|
+
* (the one structural link is to that parent) AND INHERITED owner (no
|
|
62
|
+
* owner tuple of its own; `owner from <parent>` in the FGA model).
|
|
63
|
+
* Undefined for every other kind, including kinds with additional parents
|
|
64
|
+
* whose inheritance is partial (a workflow_execution's opt-in
|
|
65
|
+
* `execution_viewer from workflow_instance` sits beside its own owner).
|
|
66
|
+
*
|
|
67
|
+
* Today exactly one kind answers: agent_execution → session (its
|
|
68
|
+
* `can_view` is `viewer or can_view from session`, and `viewer` is the
|
|
69
|
+
* session's). The list read scope reads this to carry the parent on every
|
|
70
|
+
* candidate (`ListEntryMeta.authorizationParent`, 20260913.04 T04), so a
|
|
71
|
+
* composed driver may ask its authorization backend about the parent —
|
|
72
|
+
* direct tuples — instead of the child, whose resolution walks the
|
|
73
|
+
* parent's whole set. A second kind declaring the pair gets the behavior
|
|
74
|
+
* from this declaration alone; the table pin in __tests__ makes that a
|
|
75
|
+
* reviewed change.
|
|
76
|
+
*/
|
|
77
|
+
export function inheritedAuthorizationParentOf(kind) {
|
|
78
|
+
const config = getKindMeta(kind).authorization;
|
|
79
|
+
if (config?.scopeType !== AuthorizationScopeType.PARENT ||
|
|
80
|
+
config.ownerType !== OwnerAttributionType.INHERITED) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
return config.parent;
|
|
84
|
+
}
|
|
57
85
|
/** Go GetIdPrefix: e.g. agent → "agt". */
|
|
58
86
|
export function getIdPrefix(kind) {
|
|
59
87
|
return getKindMeta(kind).idPrefix;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiresource-meta.js","sourceRoot":"","sources":["../../src/pipeline/apiresource-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,SAAS,GACV,MAAM,qFAAqF,CAAC;
|
|
1
|
+
{"version":3,"file":"apiresource-meta.js","sourceRoot":"","sources":["../../src/pipeline/apiresource-meta.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization-tuples.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/authorization-tuples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qFAAqF,CAAC;AAU3H,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EACV,0BAA0B,EAE1B,8BAA8B,EAC9B,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,4CAA4C,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,8BAA8B,GAAG,SAAS,EACrD,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CAKf;AAGD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAYnD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,qBAAqB,GAC3B,WAAW,CAAC,oBAAoB,CAAC,CAgCnC;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE,qBAAqB,EAC/B,QAAQ,EAAE,qBAAqB,GAC9B;IACD,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC7D,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAC9D,CAUA;
|
|
1
|
+
{"version":3,"file":"authorization-tuples.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/authorization-tuples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qFAAqF,CAAC;AAU3H,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EACV,0BAA0B,EAE1B,8BAA8B,EAC9B,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,4CAA4C,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,8BAA8B,GAAG,SAAS,EACrD,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CAKf;AAGD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAYnD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,qBAAqB,GAC3B,WAAW,CAAC,oBAAoB,CAAC,CAgCnC;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE,qBAAqB,EAC/B,QAAQ,EAAE,qBAAqB,GAC9B;IACD,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC7D,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAC9D,CAUA;AAgGD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE,OAAO,EACjB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,GACb,oBAAoB,GAAG,SAAS,CAkClC;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,SAAS,WAAW,EACvE,SAAS,EAAE,8BAA8B,GAAG,SAAS,EACrD,MAAM,EAAE,MAAM,GACb,YAAY,CAAC,IAAI,CAAC,CAuBpB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,SAAS,WAAW,EAChE,SAAS,EAAE,8BAA8B,GAAG,SAAS,EACrD,MAAM,EAAE,MAAM,GACb,YAAY,CAAC,IAAI,CAAC,CAiCpB;AAED,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,+BAA+B,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,IAAI,SAAS,WAAW,EAC1E,WAAW,EAAE,MAAM,GAClB,YAAY,CAAC,IAAI,CAAC,CAYpB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,SAAS,WAAW,EACpE,SAAS,EAAE,8BAA8B,GAAG,SAAS,EACrD,WAAW,EAAE,MAAM,GAClB,YAAY,CAAC,IAAI,CAAC,CAoCpB"}
|
|
@@ -18,7 +18,7 @@ export async function notifyDefaultInstanceLinked(lifecycle, event) {
|
|
|
18
18
|
import { getKindEnum, getKindMeta, getKindName } from "../apiresource-meta.js";
|
|
19
19
|
import { internalError } from "../errors.js";
|
|
20
20
|
import { EXISTING_RESOURCE_KEY } from "./load-existing.js";
|
|
21
|
-
import { metadataOf } from "./shapes.js";
|
|
21
|
+
import { metadataOf, parentIdOf } from "./shapes.js";
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
23
23
|
// Visibility shape policy — the reconciler's level→shape mapping, ported
|
|
24
24
|
// from the Java VisibilityTupleReconciler (its unit-test matrix is
|
|
@@ -81,33 +81,14 @@ export function diffVisibilityShapes(kind, oldLevel, newLevel) {
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
// ---------------------------------------------------------------------------
|
|
84
|
-
// Parent-id resolution — the ParentIdExtractorRegistry port.
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
84
|
+
// Parent-id resolution — the ParentIdExtractorRegistry port. The spec-field
|
|
85
|
+
// read itself is shapes.ts's `parentIdOf`, shared with the list read scope
|
|
86
|
+
// (20260913.04 T04) so the id a tuple is written with and the id the scope
|
|
87
|
+
// asks about are one read. This module adds the create-time rule: a
|
|
88
|
+
// missing parent fails the request.
|
|
88
89
|
// ---------------------------------------------------------------------------
|
|
89
|
-
/** proto snake_case field name → the generated property name. */
|
|
90
|
-
function camelCaseFieldName(specField) {
|
|
91
|
-
return specField.replace(/_([a-z0-9])/g, (_, ch) => ch.toUpperCase());
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Extracts the parent id named by the config from the resource's spec.
|
|
95
|
-
* Returns "" for every miss (no spec, no field, non-string value) — the
|
|
96
|
-
* caller decides whether that is fatal, exactly as Java's registry
|
|
97
|
-
* returns null and the service throws.
|
|
98
|
-
*/
|
|
99
|
-
function extractParentId(resource, specField) {
|
|
100
|
-
// Structural access is the shapes.ts idiom: spec messages are plain
|
|
101
|
-
// objects; the property name is the camelCase form of the proto field.
|
|
102
|
-
const spec = resource.spec;
|
|
103
|
-
if (spec === undefined) {
|
|
104
|
-
return "";
|
|
105
|
-
}
|
|
106
|
-
const value = spec[camelCaseFieldName(specField)];
|
|
107
|
-
return typeof value === "string" ? value : "";
|
|
108
|
-
}
|
|
109
90
|
function resolveParentLink(kind, resource, parentConfig) {
|
|
110
|
-
const parentId =
|
|
91
|
+
const parentId = parentIdOf(resource, parentConfig.specField);
|
|
111
92
|
if (parentId === "") {
|
|
112
93
|
// Java: "Parent ID required for X but not found" → the request fails.
|
|
113
94
|
throw internalError(new Error(`parent id for relation '${parentConfig.relation}' (spec field '${parentConfig.specField}') is missing on ${getKindName(kind)}`), "failed to create authorization tuples");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization-tuples.js","sourceRoot":"","sources":["../../../src/pipeline/steps/authorization-tuples.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAE/F,OAAO,EACL,sBAAsB,GAEvB,MAAM,wFAAwF,CAAC;AAgBhG;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,SAAqD,EACrD,KAAiC;IAEjC,IAAI,SAAS,EAAE,uBAAuB,KAAK,SAAS,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IACD,MAAM,SAAS,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;AACjD,CAAC;AACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"authorization-tuples.js","sourceRoot":"","sources":["../../../src/pipeline/steps/authorization-tuples.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wDAAwD,CAAC;AAE/F,OAAO,EACL,sBAAsB,GAEvB,MAAM,wFAAwF,CAAC;AAgBhG;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,SAAqD,EACrD,KAAiC;IAEjC,IAAI,SAAS,EAAE,uBAAuB,KAAK,SAAS,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IACD,MAAM,SAAS,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;AACjD,CAAC;AACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD,8EAA8E;AAC9E,yEAAyE;AACzE,mEAAmE;AACnE,qCAAqC;AACrC,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAqB,EACrB,KAA4B;IAE5B,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,UAAU,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC/C,MAAM,QAAQ,GACZ,MAAM,EAAE,uBAAuB,KAAK,IAAI,IAAI,MAAM,CAAC,WAAW,KAAK,IAAI,CAAC;IAC1E,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,qBAAqB,CAAC,cAAc;YACvC,IAAI,MAAM,EAAE,WAAW,KAAK,IAAI,EAAE,CAAC;gBACjC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC3B,CAAC;YACD,MAAM;QACR,KAAK,qBAAqB,CAAC,iBAAiB;YAC1C,IAAI,MAAM,EAAE,cAAc,KAAK,IAAI,EAAE,CAAC;gBACpC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;gBAC5B,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YACD,MAAM;QACR,KAAK,qBAAqB,CAAC,mBAAmB;YAC5C,IAAI,MAAM,EAAE,gBAAgB,KAAK,IAAI,EAAE,CAAC;gBACtC,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBAC9B,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YACD,MAAM;QACR;YACE,0DAA0D;YAC1D,MAAM;IACV,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,oBAAoB,CAClC,IAAqB,EACrB,QAA+B,EAC/B,QAA+B;IAK/B,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IACpD,CAAC;IACD,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACtD,OAAO;QACL,cAAc,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvE,cAAc,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;KACxE,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,oEAAoE;AACpE,oCAAoC;AACpC,8EAA8E;AAE9E,SAAS,iBAAiB,CACxB,IAAqB,EACrB,QAAiB,EACjB,YAAkC;IAElC,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAC9D,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACpB,sEAAsE;QACtE,MAAM,aAAa,CACjB,IAAI,KAAK,CACP,2BAA2B,YAAY,CAAC,QAAQ,kBAAkB,YAAY,CAAC,SAAS,oBAAoB,WAAW,CAAC,IAAI,CAAC,EAAE,CAChI,EACD,uCAAuC,CACxC,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,YAAY,CAAC,QAAQ;QAC/B,UAAU,EAAE,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC;QAC1C,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CACzB,IAAqB,EACrB,MAA2B,EAC3B,QAAiB,EACjB,KAAa,EACb,MAAc;IAEd,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,QAAQ,MAAM,CAAC,SAAS,EAAE,CAAC;QACzB,KAAK,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC;YACzC,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;gBACjB,MAAM,aAAa,CACjB,IAAI,KAAK,CACP,kEAAkE,WAAW,CAAC,IAAI,CAAC,EAAE,CACtF,EACD,uCAAuC,CACxC,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,IAAI,CAAC;gBACT,QAAQ,EAAE,cAAc;gBACxB,UAAU,EAAE,WAAW,CAAC,cAAc,CAAC;gBACvC,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAC;YACH,MAAM;QACR,CAAC;QACD,KAAK,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;YACnC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,aAAa,CACjB,IAAI,KAAK,CACP,GAAG,WAAW,CAAC,IAAI,CAAC,gDAAgD,CACrE,EACD,uCAAuC,CACxC,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7D,MAAM;QACR,CAAC;QACD,KAAK,sBAAsB,CAAC,UAAU;YACpC,MAAM;QACR,KAAK,sBAAsB,CAAC,QAAQ;YAClC,mEAAmE;YACnE,kEAAkE;YAClE,sDAAsD;YACtD,MAAM,CAAC,IAAI,CAAC,wDAAwD,EAAE;gBACpE,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;aACxB,CAAC,CAAC;YACH,MAAM;QACR;YACE,MAAM;IACV,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,iBAAiB,EAAE,CAAC;QAC9C,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,IAAqB,EACrB,QAAiB,EACjB,MAAsB,EACtB,MAAc;IAEd,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC;IAC/C,IACE,MAAM,KAAK,SAAS;QACpB,MAAM,CAAC,SAAS,KAAK,sBAAsB,CAAC,IAAI;QAChD,MAAM,CAAC,SAAS,KAAK,sBAAsB,CAAC,WAAW,EACvD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACjD,MAAM,aAAa,CACjB,IAAI,KAAK,CAAC,yCAAyC,CAAC,EACpD,uCAAuC,CACxC,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI;QACJ,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,KAAK,EAAE,QAAQ,CAAC,GAAG;QACnB,MAAM;QACN,gBAAgB,EAAE,MAAM,CAAC,SAAS;QAClC,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;QACjD,WAAW,EAAE,kBAAkB,CAC7B,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,QAAQ,CAAC,GAAG,EACZ,MAAM,CACP;QACD,mEAAmE;QACnE,oCAAoC;QACpC,gBAAgB,EAAE,CAAC,GAAG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;KACtE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gCAAgC,CAC9C,SAAqD,EACrD,MAAc;IAEd,OAAO;QACL,IAAI,EAAE,2BAA2B;QACjC,KAAK,CAAC,OAAO,CAAC,GAAyB;YACrC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,2BAA2B,CACvC,GAAG,CAAC,eAAe,EACnB,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,cAAc,EAClB,MAAM,CACP,CAAC;YACF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC3C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,aAAa,CAAC,KAAK,EAAE,uCAAuC,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACvC,SAAqD,EACrD,MAAc;IAEd,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,KAAK,CAAC,OAAO,CAAC,GAAyB;YACrC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,OAAO;YACT,CAAC;YACD,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAEhC,CAAC;YACd,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC;YACnC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;gBACjD,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,iBAAiB,CAAC;oBAChC,IAAI,EAAE,GAAG,CAAC,eAAe;oBACzB,UAAU,EAAE,QAAQ,CAAC,EAAE;oBACvB,KAAK,EAAE,QAAQ,CAAC,GAAG;oBACnB,MAAM,EAAE,GAAG,CAAC,cAAc;iBAC3B,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CACT,iEAAiE,EACjE;oBACE,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC;oBACtC,UAAU,EAAE,QAAQ,CAAC,EAAE;oBACvB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAC9D,CACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,uDAAuD;AACvD,MAAM,CAAC,MAAM,kBAAkB,GAAG,4BAA4B,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,UAAU,mCAAmC,CACjD,WAAmB;IAEnB,OAAO;QACL,IAAI,EAAE,8BAA8B;QACpC,OAAO,CAAC,GAAyB;YAC/B,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,CAAiC,CAAC;YACtE,GAAG,CAAC,GAAG,CACL,kBAAkB,EAClB,QAAQ,EAAE,QAAQ,EAAE,UAAU;gBAC5B,qBAAqB,CAAC,mCAAmC,CAC5D,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAC3C,SAAqD,EACrD,WAAmB;IAEnB,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,KAAK,CAAC,OAAO,CAAC,GAAyB;YACrC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,CAAiC,CAAC;YACtE,MAAM,QAAQ,GAAG,QAAQ,EAAE,QAAQ,CAAC;YACpC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;gBACjD,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GACX,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAuC;gBAClE,qBAAqB,CAAC,mCAAmC,CAAC;YAC5D,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAC7D,GAAG,CAAC,eAAe,EACnB,QAAQ,EACR,QAAQ,CAAC,UAAU,CACpB,CAAC;YACF,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/D,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,mBAAmB,CAAC;oBAClC,IAAI,EAAE,GAAG,CAAC,eAAe;oBACzB,UAAU,EAAE,QAAQ,CAAC,EAAE;oBACvB,KAAK,EAAE,QAAQ,CAAC,GAAG;oBACnB,cAAc;oBACd,cAAc;iBACf,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,aAAa,CAAC,KAAK,EAAE,oCAAoC,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -16,6 +16,24 @@ export interface HasValueShape {
|
|
|
16
16
|
export declare function metadataOf(msg: Message): ApiResourceMetadata | undefined;
|
|
17
17
|
/** The ID-wrapper's value ("" when absent) — Go HasValue.GetValue(). */
|
|
18
18
|
export declare function idValueOf(msg: Message): string;
|
|
19
|
+
/**
|
|
20
|
+
* The parent id a `kind_meta` ParentRelationConfig names by `spec_field`,
|
|
21
|
+
* read from the resource's spec — the ParentIdExtractorRegistry port, with
|
|
22
|
+
* zero hardcoded kind knowledge: the proto config names the field, this
|
|
23
|
+
* reads it structurally (spec messages are plain objects whose properties
|
|
24
|
+
* are the camelCase proto field names). Returns "" for every miss (no
|
|
25
|
+
* spec, no field, non-string value); the caller decides whether that is
|
|
26
|
+
* fatal — the tuple lifecycle throws at create (Java's registry returned
|
|
27
|
+
* null and the service threw), the list scope carries no parent.
|
|
28
|
+
*
|
|
29
|
+
* ONE reader for both: the id the authorization tuple was written with is
|
|
30
|
+
* the id the list scope later asks the authorization backend about, so
|
|
31
|
+
* the two cannot drift (20260913.04 T04). Takes `object` rather than
|
|
32
|
+
* `Message` because the list lanes hand the scope structurally-typed rows
|
|
33
|
+
* and their unit suite hands it plain objects — the same posture
|
|
34
|
+
* `metadataOf` takes behind its cast.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parentIdOf(resource: object, specField: string): string;
|
|
19
37
|
/** A message-typed field, statically narrowed so get()/set() type-check. */
|
|
20
38
|
export type MessageDescField = Extract<DescField, {
|
|
21
39
|
fieldKind: "message";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/shapes.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4DAA4D,CAAC;AAEtG,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAExE;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAG9C;AAED,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAE5E;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,GACX,gBAAgB,GAAG,SAAS,CAK9B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,OAAO,GACX,cAAc,GAAG,SAAS,CAO5B;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,OAAO,GACX,cAAc,GAAG,SAAS,CAU5B;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAI3D"}
|
|
1
|
+
{"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/shapes.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4DAA4D,CAAC;AAEtG,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAExE;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAG9C;AAOD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAOtE;AAED,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAE5E;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,GACX,gBAAgB,GAAG,SAAS,CAK9B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,OAAO,GACX,cAAc,GAAG,SAAS,CAO5B;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,OAAO,GACX,cAAc,GAAG,SAAS,CAU5B;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAI3D"}
|
|
@@ -22,6 +22,35 @@ export function idValueOf(msg) {
|
|
|
22
22
|
const value = msg.value;
|
|
23
23
|
return typeof value === "string" ? value : "";
|
|
24
24
|
}
|
|
25
|
+
/** proto snake_case field name → the generated property name. */
|
|
26
|
+
function camelCaseFieldName(specField) {
|
|
27
|
+
return specField.replace(/_([a-z0-9])/g, (_, ch) => ch.toUpperCase());
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The parent id a `kind_meta` ParentRelationConfig names by `spec_field`,
|
|
31
|
+
* read from the resource's spec — the ParentIdExtractorRegistry port, with
|
|
32
|
+
* zero hardcoded kind knowledge: the proto config names the field, this
|
|
33
|
+
* reads it structurally (spec messages are plain objects whose properties
|
|
34
|
+
* are the camelCase proto field names). Returns "" for every miss (no
|
|
35
|
+
* spec, no field, non-string value); the caller decides whether that is
|
|
36
|
+
* fatal — the tuple lifecycle throws at create (Java's registry returned
|
|
37
|
+
* null and the service threw), the list scope carries no parent.
|
|
38
|
+
*
|
|
39
|
+
* ONE reader for both: the id the authorization tuple was written with is
|
|
40
|
+
* the id the list scope later asks the authorization backend about, so
|
|
41
|
+
* the two cannot drift (20260913.04 T04). Takes `object` rather than
|
|
42
|
+
* `Message` because the list lanes hand the scope structurally-typed rows
|
|
43
|
+
* and their unit suite hands it plain objects — the same posture
|
|
44
|
+
* `metadataOf` takes behind its cast.
|
|
45
|
+
*/
|
|
46
|
+
export function parentIdOf(resource, specField) {
|
|
47
|
+
const spec = resource.spec;
|
|
48
|
+
if (spec === undefined) {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
const value = spec[camelCaseFieldName(specField)];
|
|
52
|
+
return typeof value === "string" ? value : "";
|
|
53
|
+
}
|
|
25
54
|
/**
|
|
26
55
|
* Finds a singular message field by proto name with the narrowing TS needs
|
|
27
56
|
* to give ReflectMessage out of get() — the reflection idiom every helper
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shapes.js","sourceRoot":"","sources":["../../../src/pipeline/steps/shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAgBrD;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAY;IACrC,OAAQ,GAAmC,CAAC,QAAQ,CAAC;AACvD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,SAAS,CAAC,GAAY;IACpC,MAAM,KAAK,GAAI,GAAyC,CAAC,KAAK,CAAC;IAC/D,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC;AAKD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAmB,EACnB,IAAY;IAEZ,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACtD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QACzD,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAmB,EACnB,GAAY;IAEZ,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAmB,EACnB,GAAY;IAEZ,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CACvB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CACxD,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"shapes.js","sourceRoot":"","sources":["../../../src/pipeline/steps/shapes.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/server",
|
|
3
|
-
"version": "3.15.3-dev.
|
|
3
|
+
"version": "3.15.3-dev.20260914121148",
|
|
4
4
|
"description": "The Stigmer OSS control plane as a composable library: composeServer, the extension registry and the driver seams a composition builds on. The self-contained deployable of the same server is @stigmer/server-slim.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"@connectrpc/connect-node": "^2.0.0",
|
|
63
63
|
"@kubernetes/client-node": "2.0.0",
|
|
64
64
|
"@opentelemetry/api": "^1.9.0",
|
|
65
|
-
"@stigmer/protos": "3.15.3-dev.
|
|
66
|
-
"@stigmer/temporal-codecs": "3.15.3-dev.
|
|
67
|
-
"@stigmer/zip-structure": "3.15.3-dev.
|
|
65
|
+
"@stigmer/protos": "3.15.3-dev.20260914121148",
|
|
66
|
+
"@stigmer/temporal-codecs": "3.15.3-dev.20260914121148",
|
|
67
|
+
"@stigmer/zip-structure": "3.15.3-dev.20260914121148",
|
|
68
68
|
"@temporalio/activity": "1.16.2",
|
|
69
69
|
"@temporalio/client": "1.16.2",
|
|
70
70
|
"@temporalio/common": "1.16.2",
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
* a fraction of the candidates);
|
|
15
15
|
* - the candidates carry {id, org, labels} — the driver's guest
|
|
16
16
|
* cookie rule keys on labels;
|
|
17
|
+
* - a kind whose authorization is its parent's (kind_meta PARENT scope
|
|
18
|
+
* + INHERITED owner; agent_execution → session) carries
|
|
19
|
+
* `authorizationParent` on every candidate whose spec names it, the
|
|
20
|
+
* same ResolvedParentLink the tuple lifecycle wrote; every other kind
|
|
21
|
+
* and every parentless row carries none (stigmer-cloud 20260913.04
|
|
22
|
+
* T04);
|
|
17
23
|
* - a scope failure PROPAGATES — never an empty result (the outage
|
|
18
24
|
* contract: empty means "authorized to see nothing", outage means
|
|
19
25
|
* INTERNAL through the caller's sanitized arm).
|
|
@@ -155,6 +161,138 @@ describe("restrictListByReadScope", () => {
|
|
|
155
161
|
]);
|
|
156
162
|
});
|
|
157
163
|
|
|
164
|
+
describe("authorizationParent — the parent a kind's authorization is (20260913.04 T04)", () => {
|
|
165
|
+
function recordingScope(keep: ReadonlyArray<string>) {
|
|
166
|
+
const seen: ReadonlyArray<ListEntryMeta>[] = [];
|
|
167
|
+
const scope: ListReadScope = {
|
|
168
|
+
authorizedResourceIds: () => Promise.resolve(new Set<string>()),
|
|
169
|
+
restrictListEntries: (_caller, _kind, entries) => {
|
|
170
|
+
seen.push(entries);
|
|
171
|
+
return Promise.resolve(new Set(keep));
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
return { scope, seen };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
it("an agent_execution candidate carries its session as the ResolvedParentLink the tuple lifecycle wrote", async () => {
|
|
178
|
+
const executions = [
|
|
179
|
+
{
|
|
180
|
+
metadata: { id: "aex_1", org: "acme", labels: {} },
|
|
181
|
+
spec: { sessionId: "ses_a" },
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
metadata: { id: "aex_2", org: "acme", labels: {} },
|
|
185
|
+
spec: { sessionId: "ses_a" },
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
metadata: { id: "aex_3", org: "acme", labels: {} },
|
|
189
|
+
spec: { sessionId: "ses_b" },
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
const { scope, seen } = recordingScope(["aex_1", "aex_3"]);
|
|
193
|
+
const kept = await restrictListByReadScope(
|
|
194
|
+
scope,
|
|
195
|
+
caller,
|
|
196
|
+
ApiResourceKind.agent_execution,
|
|
197
|
+
executions,
|
|
198
|
+
"",
|
|
199
|
+
);
|
|
200
|
+
expect(kept.map((r) => r.metadata.id)).toEqual(["aex_1", "aex_3"]);
|
|
201
|
+
expect(seen[0]).toEqual([
|
|
202
|
+
{
|
|
203
|
+
id: "aex_1",
|
|
204
|
+
org: "acme",
|
|
205
|
+
labels: {},
|
|
206
|
+
authorizationParent: {
|
|
207
|
+
relation: "session",
|
|
208
|
+
parentKind: ApiResourceKind.session,
|
|
209
|
+
parentId: "ses_a",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: "aex_2",
|
|
214
|
+
org: "acme",
|
|
215
|
+
labels: {},
|
|
216
|
+
authorizationParent: {
|
|
217
|
+
relation: "session",
|
|
218
|
+
parentKind: ApiResourceKind.session,
|
|
219
|
+
parentId: "ses_a",
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: "aex_3",
|
|
224
|
+
org: "acme",
|
|
225
|
+
labels: {},
|
|
226
|
+
authorizationParent: {
|
|
227
|
+
relation: "session",
|
|
228
|
+
parentKind: ApiResourceKind.session,
|
|
229
|
+
parentId: "ses_b",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
]);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("a row whose spec does not name its parent carries no parent — offered as itself, never dropped", async () => {
|
|
236
|
+
const executions = [
|
|
237
|
+
{ metadata: { id: "aex_orphan", org: "acme", labels: {} }, spec: {} },
|
|
238
|
+
{ metadata: { id: "aex_specless", org: "acme", labels: {} } },
|
|
239
|
+
];
|
|
240
|
+
const { scope, seen } = recordingScope(["aex_orphan", "aex_specless"]);
|
|
241
|
+
const kept = await restrictListByReadScope(
|
|
242
|
+
scope,
|
|
243
|
+
caller,
|
|
244
|
+
ApiResourceKind.agent_execution,
|
|
245
|
+
executions,
|
|
246
|
+
"",
|
|
247
|
+
);
|
|
248
|
+
expect(kept.map((r) => r.metadata.id)).toEqual([
|
|
249
|
+
"aex_orphan",
|
|
250
|
+
"aex_specless",
|
|
251
|
+
]);
|
|
252
|
+
expect(seen[0]).toEqual([
|
|
253
|
+
{ id: "aex_orphan", org: "acme", labels: {} },
|
|
254
|
+
{ id: "aex_specless", org: "acme", labels: {} },
|
|
255
|
+
]);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("a kind whose authorization is its own carries no parent even when its spec names one", async () => {
|
|
259
|
+
// A workflow_execution links its workflow_instance (an additional
|
|
260
|
+
// parent, partial opt-in inheritance) and owns itself: not the pair.
|
|
261
|
+
const runs = [
|
|
262
|
+
{
|
|
263
|
+
metadata: { id: "wex_1", org: "acme", labels: {} },
|
|
264
|
+
spec: { workflowInstanceId: "wfi_1" },
|
|
265
|
+
},
|
|
266
|
+
];
|
|
267
|
+
const { scope, seen } = recordingScope(["wex_1"]);
|
|
268
|
+
await restrictListByReadScope(
|
|
269
|
+
scope,
|
|
270
|
+
caller,
|
|
271
|
+
ApiResourceKind.workflow_execution,
|
|
272
|
+
runs,
|
|
273
|
+
"",
|
|
274
|
+
);
|
|
275
|
+
expect(seen[0]).toEqual([{ id: "wex_1", org: "acme", labels: {} }]);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("no scope composed = the input unchanged, the parent never resolved", async () => {
|
|
279
|
+
const executions = [
|
|
280
|
+
{
|
|
281
|
+
metadata: { id: "aex_1", org: "acme", labels: {} },
|
|
282
|
+
spec: { sessionId: "ses_a" },
|
|
283
|
+
},
|
|
284
|
+
];
|
|
285
|
+
const kept = await restrictListByReadScope(
|
|
286
|
+
undefined,
|
|
287
|
+
caller,
|
|
288
|
+
ApiResourceKind.agent_execution,
|
|
289
|
+
executions,
|
|
290
|
+
"acme",
|
|
291
|
+
);
|
|
292
|
+
expect(kept).toEqual(executions);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
158
296
|
it("a scope failure propagates — never an empty result", async () => {
|
|
159
297
|
const failing: ListReadScope = {
|
|
160
298
|
authorizedResourceIds: () => Promise.reject(new Error("fga down")),
|
|
@@ -71,21 +71,61 @@
|
|
|
71
71
|
* untouched with the org unconsulted (the first line above). Two lanes
|
|
72
72
|
* have no org on the wire (`session.list`, `apiKey.findAll`) and offer
|
|
73
73
|
* the kind until P1 entry 12 pages them.
|
|
74
|
+
* - A KIND WHOSE AUTHORIZATION IS ITS PARENT'S CARRIES THAT PARENT ON
|
|
75
|
+
* EVERY CANDIDATE (stigmer-cloud 20260913.04 T04, Q-LB-33..36). When
|
|
76
|
+
* `kind_meta` declares PARENT scope with INHERITED owner
|
|
77
|
+
* (`inheritedAuthorizationParentOf`; today agent_execution → session),
|
|
78
|
+
* the helper fills `ListEntryMeta.authorizationParent` from the row's
|
|
79
|
+
* spec through the same read the tuple lifecycle wrote the parent link
|
|
80
|
+
* with. A driver MAY answer the parent's question in place of the
|
|
81
|
+
* child's: by the model's construction the two answers are identical
|
|
82
|
+
* (the child's `owner`, `viewer` and `can_view` are all `from` the
|
|
83
|
+
* parent), and the parent's resolution is direct tuples where the
|
|
84
|
+
* child's walks the parent's whole set (OpenFGA's learned `weight2`
|
|
85
|
+
* strategy read the founder's 423 sessions per execution; that is what
|
|
86
|
+
* rolled back twice on 2026-09-14). A driver that ignores the field is
|
|
87
|
+
* still correct. The one observable difference is stated, not hidden:
|
|
88
|
+
* a row whose parent tuple never landed (the create chain's "row
|
|
89
|
+
* survives, tuple write failed" state, healed by retry) is hidden by
|
|
90
|
+
* the child's check and shown by the parent's — the row's truth, read
|
|
91
|
+
* from the field the tuple was derived from, and no widening: nobody
|
|
92
|
+
* reaches a parent through it who could not create the row in it.
|
|
74
93
|
*/
|
|
75
94
|
import type { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
76
95
|
|
|
96
|
+
// The first VALUE imports from extensions/ into pipeline/ (every other
|
|
97
|
+
// crossing is a type): two pure leaf modules that import nothing but
|
|
98
|
+
// protos, so there is no cycle. Deliberate — one resolver for the tuple
|
|
99
|
+
// writer and the list reader beats a copy in the seam.
|
|
100
|
+
import {
|
|
101
|
+
getKindEnum,
|
|
102
|
+
inheritedAuthorizationParentOf,
|
|
103
|
+
} from "../pipeline/apiresource-meta.js";
|
|
104
|
+
import { parentIdOf } from "../pipeline/steps/shapes.js";
|
|
105
|
+
|
|
77
106
|
import type { CallerIdentity } from "./identity.js";
|
|
107
|
+
import type { ResolvedParentLink } from "./resource-authorization.js";
|
|
78
108
|
|
|
79
109
|
/**
|
|
80
110
|
* The candidate metadata a post-scan lane offers the scope — the three
|
|
81
111
|
* fields the cloud driver's rules key on (id for the FGA intersection,
|
|
82
112
|
* labels for the guest cookie rule; org rides along for symmetry with
|
|
83
|
-
* the consumer-owned intersection, so a driver can log or assert on it)
|
|
113
|
+
* the consumer-owned intersection, so a driver can log or assert on it),
|
|
114
|
+
* plus the parent for kinds whose authorization is their parent's.
|
|
84
115
|
*/
|
|
85
116
|
export interface ListEntryMeta {
|
|
86
117
|
readonly id: string;
|
|
87
118
|
readonly org: string;
|
|
88
119
|
readonly labels: Readonly<Record<string, string>>;
|
|
120
|
+
/**
|
|
121
|
+
* OPTIONAL (added 20260913.04 T04): the parent this row's authorization
|
|
122
|
+
* IS — present only for a kind whose `kind_meta` is PARENT scope with
|
|
123
|
+
* INHERITED owner (the header's last contract line), the same
|
|
124
|
+
* `ResolvedParentLink` the tuple lifecycle wrote for the row. Absent for
|
|
125
|
+
* every other kind, and absent when the row's spec does not name its
|
|
126
|
+
* parent — a driver then asks about the row itself, never drops it.
|
|
127
|
+
*/
|
|
128
|
+
readonly authorizationParent?: ResolvedParentLink;
|
|
89
129
|
}
|
|
90
130
|
|
|
91
131
|
/** The scope contract (single-instance point, ExtensionDrivers.listReadScope). */
|
|
@@ -117,7 +157,10 @@ export interface ListReadScope {
|
|
|
117
157
|
|
|
118
158
|
/**
|
|
119
159
|
* The shape every stored API resource message presents to the shared
|
|
120
|
-
* helper — the metadata fields the scope's candidates are built from
|
|
160
|
+
* helper — the metadata fields the scope's candidates are built from, and
|
|
161
|
+
* the spec the parent is read from (structurally, by `kind_meta`'s
|
|
162
|
+
* `spec_field`; typed loosely because the field differs per kind and the
|
|
163
|
+
* read is `parentIdOf`'s).
|
|
121
164
|
*/
|
|
122
165
|
export interface ScopedListResource {
|
|
123
166
|
metadata?: {
|
|
@@ -125,6 +168,7 @@ export interface ScopedListResource {
|
|
|
125
168
|
org?: string;
|
|
126
169
|
labels?: Record<string, string>;
|
|
127
170
|
};
|
|
171
|
+
spec?: object;
|
|
128
172
|
}
|
|
129
173
|
|
|
130
174
|
/**
|
|
@@ -141,8 +185,11 @@ export interface ScopedListResource {
|
|
|
141
185
|
* permission-bounded across orgs, verified per lane in the entry's
|
|
142
186
|
* census — lanes Java does not org-narrow pass ""), then to the kept
|
|
143
187
|
* ids. The same set either way (both are per-row predicates); the
|
|
144
|
-
* order is the header's last contract line: the scope sees
|
|
145
|
-
* rows, not the kind's
|
|
188
|
+
* order is the header's second-to-last contract line: the scope sees
|
|
189
|
+
* the org's rows, not the kind's;
|
|
190
|
+
* - the candidates of a kind whose authorization is its parent's carry
|
|
191
|
+
* `authorizationParent` (the header's last line), resolved once per
|
|
192
|
+
* call from `kind_meta` and once per row from the spec.
|
|
146
193
|
*/
|
|
147
194
|
export async function restrictListByReadScope<T extends ScopedListResource>(
|
|
148
195
|
scope: ListReadScope | undefined,
|
|
@@ -158,11 +205,42 @@ export async function restrictListByReadScope<T extends ScopedListResource>(
|
|
|
158
205
|
requestOrg === ""
|
|
159
206
|
? resources
|
|
160
207
|
: resources.filter((resource) => resource.metadata?.org === requestOrg);
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
208
|
+
const parentOf = authorizationParentReader(kind);
|
|
209
|
+
const entries: ListEntryMeta[] = offered.map((resource) => {
|
|
210
|
+
const entry: ListEntryMeta = {
|
|
211
|
+
id: resource.metadata?.id ?? "",
|
|
212
|
+
org: resource.metadata?.org ?? "",
|
|
213
|
+
labels: resource.metadata?.labels ?? {},
|
|
214
|
+
};
|
|
215
|
+
const authorizationParent = parentOf(resource);
|
|
216
|
+
return authorizationParent === undefined
|
|
217
|
+
? entry
|
|
218
|
+
: { ...entry, authorizationParent };
|
|
219
|
+
});
|
|
166
220
|
const keptIds = await scope.restrictListEntries(caller, kind, entries);
|
|
167
221
|
return offered.filter((resource) => keptIds.has(resource.metadata?.id ?? ""));
|
|
168
222
|
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The per-row parent reader for `kind`: a constant `undefined` for the 27
|
|
226
|
+
* kinds whose authorization is their own, and for the kind whose
|
|
227
|
+
* authorization is its parent's, the `ResolvedParentLink` the tuple
|
|
228
|
+
* lifecycle wrote — or undefined for a row whose spec does not name it
|
|
229
|
+
* (never a throw: a list is not a create). `kind_meta` is consulted once
|
|
230
|
+
* per call, not once per row.
|
|
231
|
+
*/
|
|
232
|
+
function authorizationParentReader(
|
|
233
|
+
kind: ApiResourceKind,
|
|
234
|
+
): (resource: ScopedListResource) => ResolvedParentLink | undefined {
|
|
235
|
+
const parent = inheritedAuthorizationParentOf(kind);
|
|
236
|
+
if (parent === undefined) {
|
|
237
|
+
return () => undefined;
|
|
238
|
+
}
|
|
239
|
+
const parentKind = getKindEnum(parent.kind);
|
|
240
|
+
return (resource) => {
|
|
241
|
+
const parentId = parentIdOf(resource, parent.specField);
|
|
242
|
+
return parentId === ""
|
|
243
|
+
? undefined
|
|
244
|
+
: { relation: parent.relation, parentKind, parentId };
|
|
245
|
+
};
|
|
246
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins `inheritedAuthorizationParentOf` (pipeline/apiresource-meta.ts):
|
|
3
|
+
* the kind_meta question "is this kind's authorization its parent's
|
|
4
|
+
* whole?", answered by the PARENT-scope + INHERITED-owner pair and by
|
|
5
|
+
* nothing else (stigmer-cloud 20260913.04 T04, Q-LB-34).
|
|
6
|
+
*
|
|
7
|
+
* The table below is the WHOLE kind enum, on purpose. The list read scope
|
|
8
|
+
* carries `authorizationParent` for exactly the kinds this function names,
|
|
9
|
+
* and a composed driver asks its authorization backend about the parent
|
|
10
|
+
* in the child's place — so a second kind joining the set is a change in
|
|
11
|
+
* what that driver checks, and must arrive as a reviewed edit of this
|
|
12
|
+
* table, never as a silent consequence of a kind_meta edit. Kinds with
|
|
13
|
+
* additional parents whose inheritance is partial (workflow_execution's
|
|
14
|
+
* opt-in `execution_viewer from workflow_instance`, agent_instance's
|
|
15
|
+
* `viewer from default_of`) own themselves and are pinned as `undefined`.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, expect, it } from "vitest";
|
|
18
|
+
|
|
19
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
20
|
+
|
|
21
|
+
import { inheritedAuthorizationParentOf } from "../apiresource-meta.js";
|
|
22
|
+
|
|
23
|
+
describe("inheritedAuthorizationParentOf — the parent a kind's authorization is", () => {
|
|
24
|
+
it("agent_execution's authorization is its session's: relation `session`, spec field `session_id`", () => {
|
|
25
|
+
const parent = inheritedAuthorizationParentOf(
|
|
26
|
+
ApiResourceKind.agent_execution,
|
|
27
|
+
);
|
|
28
|
+
expect(parent).toBeDefined();
|
|
29
|
+
expect(parent?.kind).toBe("session");
|
|
30
|
+
expect(parent?.relation).toBe("session");
|
|
31
|
+
expect(parent?.specField).toBe("session_id");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("every other kind's authorization is its own (the whole enum, pinned)", () => {
|
|
35
|
+
const inherited = Object.values(ApiResourceKind)
|
|
36
|
+
.filter((value): value is ApiResourceKind => typeof value === "number")
|
|
37
|
+
.filter((kind) => kind !== ApiResourceKind.api_resource_kind_unknown)
|
|
38
|
+
.filter((kind) => inheritedAuthorizationParentOf(kind) !== undefined)
|
|
39
|
+
.map((kind) => ApiResourceKind[kind]);
|
|
40
|
+
expect(inherited).toEqual(["agent_execution"]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it.each([
|
|
44
|
+
ApiResourceKind.workflow_execution,
|
|
45
|
+
ApiResourceKind.agent_instance,
|
|
46
|
+
ApiResourceKind.workflow_instance,
|
|
47
|
+
ApiResourceKind.memory,
|
|
48
|
+
])("a kind with an additional, partial parent owns itself: %s", (kind) => {
|
|
49
|
+
expect(inheritedAuthorizationParentOf(kind)).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -53,6 +53,11 @@ import {
|
|
|
53
53
|
kind_meta,
|
|
54
54
|
} from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
55
55
|
import type { ApiResourceKindMeta } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
56
|
+
import {
|
|
57
|
+
AuthorizationScopeType,
|
|
58
|
+
OwnerAttributionType,
|
|
59
|
+
} from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/authorization_config_pb";
|
|
60
|
+
import type { ParentRelationConfig } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/authorization_config_pb";
|
|
56
61
|
import type { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
57
62
|
import { ServerEdition } from "@stigmer/protos/ai/stigmer/platform/v1/server_info_pb";
|
|
58
63
|
|
|
@@ -65,6 +70,38 @@ export function getKindMeta(kind: ApiResourceKind): ApiResourceKindMeta {
|
|
|
65
70
|
return getOption(valueDesc, kind_meta);
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
/**
|
|
74
|
+
* The parent a kind's authorization IS — the `kind_meta` declaration that
|
|
75
|
+
* a kind's permissions are its parent's whole, not its own: PARENT scope
|
|
76
|
+
* (the one structural link is to that parent) AND INHERITED owner (no
|
|
77
|
+
* owner tuple of its own; `owner from <parent>` in the FGA model).
|
|
78
|
+
* Undefined for every other kind, including kinds with additional parents
|
|
79
|
+
* whose inheritance is partial (a workflow_execution's opt-in
|
|
80
|
+
* `execution_viewer from workflow_instance` sits beside its own owner).
|
|
81
|
+
*
|
|
82
|
+
* Today exactly one kind answers: agent_execution → session (its
|
|
83
|
+
* `can_view` is `viewer or can_view from session`, and `viewer` is the
|
|
84
|
+
* session's). The list read scope reads this to carry the parent on every
|
|
85
|
+
* candidate (`ListEntryMeta.authorizationParent`, 20260913.04 T04), so a
|
|
86
|
+
* composed driver may ask its authorization backend about the parent —
|
|
87
|
+
* direct tuples — instead of the child, whose resolution walks the
|
|
88
|
+
* parent's whole set. A second kind declaring the pair gets the behavior
|
|
89
|
+
* from this declaration alone; the table pin in __tests__ makes that a
|
|
90
|
+
* reviewed change.
|
|
91
|
+
*/
|
|
92
|
+
export function inheritedAuthorizationParentOf(
|
|
93
|
+
kind: ApiResourceKind,
|
|
94
|
+
): ParentRelationConfig | undefined {
|
|
95
|
+
const config = getKindMeta(kind).authorization;
|
|
96
|
+
if (
|
|
97
|
+
config?.scopeType !== AuthorizationScopeType.PARENT ||
|
|
98
|
+
config.ownerType !== OwnerAttributionType.INHERITED
|
|
99
|
+
) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
return config.parent;
|
|
103
|
+
}
|
|
104
|
+
|
|
68
105
|
/** Go GetIdPrefix: e.g. agent → "agt". */
|
|
69
106
|
export function getIdPrefix(kind: ApiResourceKind): string {
|
|
70
107
|
return getKindMeta(kind).idPrefix;
|
|
@@ -78,7 +78,7 @@ import type { PipelineStep } from "../pipeline.js";
|
|
|
78
78
|
import type { RequestContext } from "../request-context.js";
|
|
79
79
|
import { EXISTING_RESOURCE_KEY } from "./load-existing.js";
|
|
80
80
|
import type { HasMetadataShape } from "./shapes.js";
|
|
81
|
-
import { metadataOf } from "./shapes.js";
|
|
81
|
+
import { metadataOf, parentIdOf } from "./shapes.js";
|
|
82
82
|
|
|
83
83
|
// ---------------------------------------------------------------------------
|
|
84
84
|
// Visibility shape policy — the reconciler's level→shape mapping, ported
|
|
@@ -156,40 +156,19 @@ export function diffVisibilityShapes(
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// ---------------------------------------------------------------------------
|
|
159
|
-
// Parent-id resolution — the ParentIdExtractorRegistry port.
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
159
|
+
// Parent-id resolution — the ParentIdExtractorRegistry port. The spec-field
|
|
160
|
+
// read itself is shapes.ts's `parentIdOf`, shared with the list read scope
|
|
161
|
+
// (20260913.04 T04) so the id a tuple is written with and the id the scope
|
|
162
|
+
// asks about are one read. This module adds the create-time rule: a
|
|
163
|
+
// missing parent fails the request.
|
|
163
164
|
// ---------------------------------------------------------------------------
|
|
164
165
|
|
|
165
|
-
/** proto snake_case field name → the generated property name. */
|
|
166
|
-
function camelCaseFieldName(specField: string): string {
|
|
167
|
-
return specField.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Extracts the parent id named by the config from the resource's spec.
|
|
172
|
-
* Returns "" for every miss (no spec, no field, non-string value) — the
|
|
173
|
-
* caller decides whether that is fatal, exactly as Java's registry
|
|
174
|
-
* returns null and the service throws.
|
|
175
|
-
*/
|
|
176
|
-
function extractParentId(resource: Message, specField: string): string {
|
|
177
|
-
// Structural access is the shapes.ts idiom: spec messages are plain
|
|
178
|
-
// objects; the property name is the camelCase form of the proto field.
|
|
179
|
-
const spec = (resource as unknown as { spec?: Record<string, unknown> }).spec;
|
|
180
|
-
if (spec === undefined) {
|
|
181
|
-
return "";
|
|
182
|
-
}
|
|
183
|
-
const value = spec[camelCaseFieldName(specField)];
|
|
184
|
-
return typeof value === "string" ? value : "";
|
|
185
|
-
}
|
|
186
|
-
|
|
187
166
|
function resolveParentLink(
|
|
188
167
|
kind: ApiResourceKind,
|
|
189
168
|
resource: Message,
|
|
190
169
|
parentConfig: ParentRelationConfig,
|
|
191
170
|
): ResolvedParentLink {
|
|
192
|
-
const parentId =
|
|
171
|
+
const parentId = parentIdOf(resource, parentConfig.specField);
|
|
193
172
|
if (parentId === "") {
|
|
194
173
|
// Java: "Parent ID required for X but not found" → the request fails.
|
|
195
174
|
throw internalError(
|
|
@@ -39,6 +39,37 @@ export function idValueOf(msg: Message): string {
|
|
|
39
39
|
return typeof value === "string" ? value : "";
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** proto snake_case field name → the generated property name. */
|
|
43
|
+
function camelCaseFieldName(specField: string): string {
|
|
44
|
+
return specField.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The parent id a `kind_meta` ParentRelationConfig names by `spec_field`,
|
|
49
|
+
* read from the resource's spec — the ParentIdExtractorRegistry port, with
|
|
50
|
+
* zero hardcoded kind knowledge: the proto config names the field, this
|
|
51
|
+
* reads it structurally (spec messages are plain objects whose properties
|
|
52
|
+
* are the camelCase proto field names). Returns "" for every miss (no
|
|
53
|
+
* spec, no field, non-string value); the caller decides whether that is
|
|
54
|
+
* fatal — the tuple lifecycle throws at create (Java's registry returned
|
|
55
|
+
* null and the service threw), the list scope carries no parent.
|
|
56
|
+
*
|
|
57
|
+
* ONE reader for both: the id the authorization tuple was written with is
|
|
58
|
+
* the id the list scope later asks the authorization backend about, so
|
|
59
|
+
* the two cannot drift (20260913.04 T04). Takes `object` rather than
|
|
60
|
+
* `Message` because the list lanes hand the scope structurally-typed rows
|
|
61
|
+
* and their unit suite hands it plain objects — the same posture
|
|
62
|
+
* `metadataOf` takes behind its cast.
|
|
63
|
+
*/
|
|
64
|
+
export function parentIdOf(resource: object, specField: string): string {
|
|
65
|
+
const spec = (resource as { spec?: Record<string, unknown> }).spec;
|
|
66
|
+
if (spec === undefined) {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
const value = spec[camelCaseFieldName(specField)];
|
|
70
|
+
return typeof value === "string" ? value : "";
|
|
71
|
+
}
|
|
72
|
+
|
|
42
73
|
/** A message-typed field, statically narrowed so get()/set() type-check. */
|
|
43
74
|
export type MessageDescField = Extract<DescField, { fieldKind: "message" }>;
|
|
44
75
|
|