@vxil/cli 0.16.1 → 0.18.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.
- package/dist/_vxil-feature-configs-hooks.d.ts +41 -0
- package/dist/_vxil-feature-configs.d.ts +6 -2
- package/dist/config.d.ts +15 -0
- package/dist/vxil.js +616 -35
- package/package.json +4 -4
|
@@ -64,6 +64,17 @@ export declare class HookEvalError extends Error {
|
|
|
64
64
|
/** Thrown by a `validate` hook whose expression is falsy — maps to a clean 422. */
|
|
65
65
|
export declare class HookRejection extends Error {
|
|
66
66
|
}
|
|
67
|
+
/** A WRITE `validate` hook whose expression could not be evaluated (null
|
|
68
|
+
* arithmetic, a missing operand, a bad date …). Still a HookEvalError — the
|
|
69
|
+
* write is refused, never let through — but it carries the hook's id and its
|
|
70
|
+
* declared `message`, so the API can show the end user the tenant's own copy
|
|
71
|
+
* while keeping the engine detail for the developer. `message` stays the
|
|
72
|
+
* engine detail. */
|
|
73
|
+
export declare class HookValidateFault extends HookEvalError {
|
|
74
|
+
readonly hookId: string;
|
|
75
|
+
readonly tenantMessage: string | undefined;
|
|
76
|
+
constructor(hookId: string, tenantMessage: string | undefined, detail: string);
|
|
77
|
+
}
|
|
67
78
|
/** Parse a hook expression into an AST. Throws HookParseError on any malformed input. */
|
|
68
79
|
export declare function parseExpr(src: string): Node;
|
|
69
80
|
/** Walk the AST and reject anything outside the allow-list, plus depth bounds.
|
|
@@ -157,6 +168,36 @@ export type InferredType = 'number' | 'string' | 'boolean' | 'null' | 'unknown';
|
|
|
157
168
|
/** Best-effort static type of an expression's result (its output). 'unknown' for
|
|
158
169
|
* member access / coalesce / mixed ternaries — never a false-positive mismatch. */
|
|
159
170
|
export declare function inferType(nd: Node): InferredType;
|
|
171
|
+
/** One declared field, as the typing pass needs it (cms field type + required). */
|
|
172
|
+
export interface HookFieldInfo {
|
|
173
|
+
type: string;
|
|
174
|
+
required?: boolean;
|
|
175
|
+
}
|
|
176
|
+
/** collection → field → declared info. */
|
|
177
|
+
export type HookFieldTypes = Record<string, Record<string, HookFieldInfo>>;
|
|
178
|
+
/** Type one parsed expression against one collection's declared fields.
|
|
179
|
+
* `errors` are shapes that can never work (refuse); `warnings` are shapes
|
|
180
|
+
* that work only for some stored values (json vs a scalar, `+` on a text
|
|
181
|
+
* field) and arithmetic on an optional field with no coalesce() — it faults
|
|
182
|
+
* the hook whenever the field is missing. `present` lists fields a derive sets first. */
|
|
183
|
+
export declare function checkHookExprTypes(root: Node, fields: Record<string, HookFieldInfo> | undefined, opts: {
|
|
184
|
+
kind: HookDef['kind'];
|
|
185
|
+
event: HookDef['event'];
|
|
186
|
+
present?: ReadonlySet<string> | undefined;
|
|
187
|
+
}): {
|
|
188
|
+
errors: string[];
|
|
189
|
+
warnings: string[];
|
|
190
|
+
};
|
|
191
|
+
/** Project declared collections (`{ <name>: { fields: { <f>: { type, required? } } } }`
|
|
192
|
+
* — the vxil.config / apply-bundle shape) onto HookFieldTypes. Malformed
|
|
193
|
+
* entries are skipped: no declared field means no verdict. */
|
|
194
|
+
export declare function hookFieldTypesOf(collections: unknown): HookFieldTypes;
|
|
195
|
+
/** Type every hook whose collection the caller declares (see checkHookExprTypes).
|
|
196
|
+
* Unparseable expressions are skipped — validateHookDef already reports them. */
|
|
197
|
+
export declare function checkHookTypes(hooks: Record<string, HookDef> | undefined, fieldTypes: HookFieldTypes): {
|
|
198
|
+
errors: string[];
|
|
199
|
+
warnings: string[];
|
|
200
|
+
};
|
|
160
201
|
export type HlKind = 'num' | 'str' | 'fn' | 'var' | 'kw' | 'op' | 'ident' | 'ws' | 'err';
|
|
161
202
|
/** Token-level highlighter over the FULL source (whitespace + bad chars kept),
|
|
162
203
|
* so a dashboard editor can render a colored overlay behind a textarea. */
|
|
@@ -78,9 +78,10 @@ export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObj
|
|
|
78
78
|
maxAttempts: import("@sinclair/typebox").TInteger;
|
|
79
79
|
backoff: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"exponential">, import("@sinclair/typebox").TLiteral<"linear">]>;
|
|
80
80
|
}>>;
|
|
81
|
-
suppression: import("@sinclair/typebox").TObject<{
|
|
81
|
+
suppression: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
82
82
|
softBounceThreshold: import("@sinclair/typebox").TInteger;
|
|
83
|
-
|
|
83
|
+
testRecipients: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
84
|
+
}>>;
|
|
84
85
|
rateLimit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
85
86
|
perDay: import("@sinclair/typebox").TInteger;
|
|
86
87
|
perTenantSec: import("@sinclair/typebox").TInteger;
|
|
@@ -832,6 +833,9 @@ export declare const TEST_RECIPIENT_EMAIL_RE: RegExp;
|
|
|
832
833
|
export declare const TEST_RECIPIENT_GLOB_RE: RegExp;
|
|
833
834
|
export declare const TEST_RECIPIENT_E164_RE: RegExp;
|
|
834
835
|
export declare function isTestRecipientPattern(entry: string): boolean;
|
|
836
|
+
/** The MAIL subset of the grammar (notifications suppression.testRecipients):
|
|
837
|
+
* an exact email or an `*@domain` glob. */
|
|
838
|
+
export declare function isMailTestRecipientPattern(entry: string): boolean;
|
|
835
839
|
/** True iff `identifier` (an email today) matches one configured entry:
|
|
836
840
|
* exact (case-insensitive) or the `*@domain` glob. +E.164 entries never match
|
|
837
841
|
* an email. */
|
package/dist/config.d.ts
CHANGED
|
@@ -106,6 +106,21 @@ export interface CollectionDef {
|
|
|
106
106
|
* with a regression test on each — an attribute read by no enforcing path ships
|
|
107
107
|
* inert (the validation.unique / owner_field precedent). */
|
|
108
108
|
public?: boolean;
|
|
109
|
+
/** What a VERIFIED end user (a thin-client key + a session) may do on this
|
|
110
|
+
* collection (https://vxil.com/docs/guide/09-security-and-multitenancy):
|
|
111
|
+
* 'readwrite' (default) — read and write (owner-scoped when ownerField is set);
|
|
112
|
+
* 'read' — read only (owner-scoped as above); every end-user write
|
|
113
|
+
* (create, update, $inc, delete, publish, transaction step,
|
|
114
|
+
* filtered delete, batch write — and a delete elsewhere
|
|
115
|
+
* whose on_delete cascade / set_null would change a row
|
|
116
|
+
* here) is `403 server_only`;
|
|
117
|
+
* 'none' — no end-user reads or writes at all, even with ownerField.
|
|
118
|
+
* Server keys (and platform writes such as a jobs status mirror) are never
|
|
119
|
+
* affected. Stored on the collection (model data, not a config leaf) and
|
|
120
|
+
* carried by BOTH push paths; widening ('read'/'none' → 'readwrite') needs
|
|
121
|
+
* `vxil push --allow-destructive`. Any other value fails `vxil plan` /
|
|
122
|
+
* `vxil push` (it never falls back to 'readwrite'). */
|
|
123
|
+
endUserAccess?: 'readwrite' | 'read' | 'none';
|
|
109
124
|
/** Per-record action buttons (guide ch. 4): `[{ key, label, fn }]`, stored on
|
|
110
125
|
* the collection (model data, not a config leaf). The dashboard renders one
|
|
111
126
|
* button per action on each record row; `POST /v1/cms/items/:coll/:id/actions/
|