@vxil/cli 0.18.0 → 0.20.0
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.
|
@@ -10,12 +10,16 @@ export declare const HOOK_LIMITS: {
|
|
|
10
10
|
readonly maxReadHooksPerCollection: 10;
|
|
11
11
|
};
|
|
12
12
|
/** The ONLY root variables an expression may reference. `caller` is the VERIFIED
|
|
13
|
-
* end-user principal (https://vxil.com/docs/guide/09-security-and-multitenancy) — read-only
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* end-user principal (https://vxil.com/docs/guide/09-security-and-multitenancy) — read-only.
|
|
14
|
+
* It carries `caller.endUserId` (the verified session sub, or null),
|
|
15
|
+
* `caller.principal` ('end_user' | 'tenant') and `caller.roles` (the VERIFIED
|
|
16
|
+
* session role claims of the signed-in session — never a body or header
|
|
17
|
+
* value; null in server-caller mode). READ hooks always get it
|
|
18
|
+
* (server mode: `{ endUserId: null, principal: 'tenant', roles: null }`).
|
|
19
|
+
* WRITE hooks get it in verified end-user mode only; on a
|
|
20
|
+
* server write it is null, exactly like `before` on a create — so a role rule
|
|
21
|
+
* reads `isNull(caller) || contains(caller.roles, 'manager')`. `contains` on
|
|
22
|
+
* an array is exact element membership. */
|
|
19
23
|
export declare const HOOK_ROOT_VARS: readonly ["item", "before", "now", "caller"];
|
|
20
24
|
/** The ONLY callable functions. Each is pure + deterministic + bounded. */
|
|
21
25
|
export declare const HOOK_FUNCTIONS: readonly ["min", "max", "abs", "round", "floor", "ceil", "sqrt", "pow", "sign", "len", "lower", "upper", "trim", "substr", "contains", "startsWith", "endsWith", "concat", "coalesce", "ifNull", "not", "isNull", "number", "string", "bool", "daysBetween", "yearsBetween"];
|
|
@@ -80,20 +84,25 @@ export declare function parseExpr(src: string): Node;
|
|
|
80
84
|
/** Walk the AST and reject anything outside the allow-list, plus depth bounds.
|
|
81
85
|
* Returns an array of human-readable errors (empty = safe). Pure, no eval. */
|
|
82
86
|
export declare function validateAst(root: Node): string[];
|
|
83
|
-
/** The VERIFIED caller/session context for
|
|
84
|
-
*
|
|
85
|
-
*
|
|
87
|
+
/** The VERIFIED caller/session context for Lane-A hooks (guide ch. 7).
|
|
88
|
+
* Read-only. Read hooks always get it; write hooks get it in verified end-user
|
|
89
|
+
* mode only (a server write passes null — see HOOK_ROOT_VARS). */
|
|
86
90
|
export interface HookCaller {
|
|
87
91
|
/** the verified end-user session sub, or null in server-caller mode. */
|
|
88
92
|
endUserId: string | null;
|
|
89
93
|
/** 'end_user' when a session was verified at the edge, else 'tenant'. */
|
|
90
94
|
principal: 'end_user' | 'tenant';
|
|
95
|
+
/** the VERIFIED session role claims of the signed-in session; `[]` for a
|
|
96
|
+
* session without roles, null in server-caller mode. Optional so an older
|
|
97
|
+
* caller object still reads
|
|
98
|
+
* as "no roles". */
|
|
99
|
+
roles?: readonly string[] | null;
|
|
91
100
|
}
|
|
92
101
|
export interface HookContext {
|
|
93
102
|
item: Record<string, unknown>;
|
|
94
103
|
before: Record<string, unknown> | null;
|
|
95
104
|
now: string;
|
|
96
|
-
/** verified caller/session context
|
|
105
|
+
/** verified caller/session context; null on a server write. */
|
|
97
106
|
caller?: HookCaller | null;
|
|
98
107
|
}
|
|
99
108
|
/** A mutable step counter SHARED across many evalExpr calls (the read path's
|
|
@@ -127,7 +136,10 @@ export interface HookRunResult {
|
|
|
127
136
|
* Mutates a CLONE: derive hooks set fields; a validate hook that returns falsy
|
|
128
137
|
* throws HookRejection (→ the caller maps to a 422 and the tx rolls back).
|
|
129
138
|
* Throws HookEvalError on a runtime fault (also a clean 422, never a 500). */
|
|
130
|
-
export declare function runWriteHooks(hooks: Record<string, HookDef> | undefined, collection: string, phase: 'create' | 'update', data: Record<string, unknown>, before: Record<string, unknown> | null, now: string
|
|
139
|
+
export declare function runWriteHooks(hooks: Record<string, HookDef> | undefined, collection: string, phase: 'create' | 'update', data: Record<string, unknown>, before: Record<string, unknown> | null, now: string,
|
|
140
|
+
/** the VERIFIED end-user caller (endUserId + principal + roles) in
|
|
141
|
+
* end-user mode; omitted / null on a server write (expressions see null). */
|
|
142
|
+
caller?: HookCaller | null): HookRunResult;
|
|
131
143
|
export interface ReadHookResult {
|
|
132
144
|
/** Surviving rows in input order. Each is a SHALLOW COPY with a copied `data`
|
|
133
145
|
* bag — the caller's rows are never mutated and nothing here is persisted. */
|
|
@@ -154,8 +166,8 @@ export interface ReadHookResult {
|
|
|
154
166
|
* fire here, and read events never fire in runWriteHooks. */
|
|
155
167
|
export declare function runReadHooks(hooks: Record<string, HookDef> | undefined, collection: string, rows: ReadonlyArray<Record<string, unknown>>, now: string,
|
|
156
168
|
/** verified caller/session context (guide ch. 7) — read-only, exposed to
|
|
157
|
-
* expressions as `caller.endUserId` / `caller.principal
|
|
158
|
-
* (server-caller mode). */
|
|
169
|
+
* expressions as `caller.endUserId` / `caller.principal` / `caller.roles`.
|
|
170
|
+
* Omitted ⇒ null (server-caller mode). */
|
|
159
171
|
caller?: HookCaller | null): ReadHookResult;
|
|
160
172
|
export interface ExprRef {
|
|
161
173
|
root: 'item' | 'before' | 'now' | 'caller';
|
|
@@ -35,4 +35,22 @@ interface CdcRuleLike {
|
|
|
35
35
|
}
|
|
36
36
|
/** Cross-field rule for CmsConfig.cdc (called from validateFeatureConfig). */
|
|
37
37
|
export declare function validateCdcConfig(cdc: Record<string, CdcRuleLike> | undefined): string[];
|
|
38
|
+
/** What a payload:'full' change-data rule must know about its target
|
|
39
|
+
* collection's read gates. `readRoleFields`: fields with a non-empty
|
|
40
|
+
* `readRoles`; `endUserAccess`: the collection's end-user access mode. */
|
|
41
|
+
export interface CdcCollectionGates {
|
|
42
|
+
readRoleFields?: readonly string[];
|
|
43
|
+
endUserAccess?: 'readwrite' | 'read' | 'none';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A `payload: 'full'` change-data rule inlines the stored row into a realtime
|
|
47
|
+
* frame that every subscriber of the channel receives — it does not go through
|
|
48
|
+
* the read projection. So it must never target a collection whose reads are
|
|
49
|
+
* gated: a field with `readRoles` (the frame would carry the gated value), or
|
|
50
|
+
* an `endUserAccess` of 'read' / 'none' (the frame would bypass the
|
|
51
|
+
* collection's end-user gate). Pure; one problem line per offending ENABLED
|
|
52
|
+
* rule (an unknown collection is skipped). The safe shape is `payload: 'ids'`
|
|
53
|
+
* and a re-read through the cms, where every gate applies.
|
|
54
|
+
*/
|
|
55
|
+
export declare function cdcFullFramesOverGatedCollections(cdc: unknown, collections: Record<string, CdcCollectionGates>): string[];
|
|
38
56
|
export {};
|
|
@@ -218,6 +218,7 @@ declare const OidcProviderSchema: import("@sinclair/typebox").TObject<{
|
|
|
218
218
|
email: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
219
219
|
name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
220
220
|
roles: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
221
|
+
rolePrefix: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
221
222
|
}>>;
|
|
222
223
|
allowedDomains: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
223
224
|
autoLink: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
@@ -275,6 +276,7 @@ export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
275
276
|
email: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
276
277
|
name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
277
278
|
roles: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
279
|
+
rolePrefix: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
278
280
|
}>>;
|
|
279
281
|
allowedDomains: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
280
282
|
autoLink: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
@@ -319,6 +321,8 @@ export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
319
321
|
breachedPasswords: import("@sinclair/typebox").TBoolean;
|
|
320
322
|
captchaSecretRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
321
323
|
allowedRedirectOrigins: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
324
|
+
inviteOnly: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
325
|
+
allowedEmailDomains: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
322
326
|
}>>;
|
|
323
327
|
}>;
|
|
324
328
|
/** The security bag as persisted (present ⇒ leaf defaults applied). */
|
|
@@ -633,6 +637,7 @@ export declare const RagConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
633
637
|
topK: import("@sinclair/typebox").TInteger;
|
|
634
638
|
mode: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"hybrid">, import("@sinclair/typebox").TLiteral<"vector">, import("@sinclair/typebox").TLiteral<"keyword">]>;
|
|
635
639
|
minScore: import("@sinclair/typebox").TNumber;
|
|
640
|
+
minSimilarity: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
636
641
|
rerank: import("@sinclair/typebox").TBoolean;
|
|
637
642
|
}>;
|
|
638
643
|
boosts: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
|
package/dist/config.d.ts
CHANGED
|
@@ -121,6 +121,17 @@ export interface CollectionDef {
|
|
|
121
121
|
* `vxil push --allow-destructive`. Any other value fails `vxil plan` /
|
|
122
122
|
* `vxil push` (it never falls back to 'readwrite'). */
|
|
123
123
|
endUserAccess?: 'readwrite' | 'read' | 'none';
|
|
124
|
+
/** Role slugs one of which a VERIFIED end user must hold to WRITE this
|
|
125
|
+
* collection (create, update, $inc, delete, publish, transaction step,
|
|
126
|
+
* filtered delete, batch write, and an end-user delete's cascade into it) —
|
|
127
|
+
* otherwise `403 role_required`. Reads are unaffected. Roles are the
|
|
128
|
+
* session's verified role claims (its orgs roles — the
|
|
129
|
+
* same ones `readRoles` and `caller.roles` in hooks see). Server keys are
|
|
130
|
+
* never affected. Absent / `[]` = ungated. Up to 16 slugs
|
|
131
|
+
* (`^[a-z0-9][a-z0-9_-]{0,31}$`). Stored on the collection (model data, not
|
|
132
|
+
* a config leaf) and carried by BOTH push paths; widening (clearing, or
|
|
133
|
+
* adding a role) needs `vxil push --allow-destructive`. */
|
|
134
|
+
writeRoles?: string[];
|
|
124
135
|
/** Per-record action buttons (guide ch. 4): `[{ key, label, fn }]`, stored on
|
|
125
136
|
* the collection (model data, not a config leaf). The dashboard renders one
|
|
126
137
|
* button per action on each record row; `POST /v1/cms/items/:coll/:id/actions/
|