@vxil/cli 0.5.0 → 0.5.2
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/README.md +6 -2
- package/dist/_vxil-feature-configs.d.ts +25 -3
- package/dist/config.d.ts +44 -25
- package/dist/vxil.js +308 -68
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,8 +44,12 @@ Verbs: `init templates quickstart try login link plan push pull diff gen doctor
|
|
|
44
44
|
**Every verb names its target.** `--dev` selects the dev slot, `--target <name>` a named slot bound by `vxil link <slug> --as <name>` (which merges into the existing binding rather than replacing it). The selector is **fail-closed** — an unbound slot exits 1 telling you the fix, and never falls through to another tenant — and every write verb prints a target banner (slug + env, never a key) before it acts.
|
|
45
45
|
|
|
46
46
|
- `vxil plan --explain` — the config plan with every leaf tagged *declared* or *default*.
|
|
47
|
-
- `vxil
|
|
48
|
-
- `vxil
|
|
47
|
+
- `vxil push [--skip-functions]` — apply schema → config → functions; a project below the functions plan is stopped *before anything is pushed*, and `--skip-functions` pushes the rest.
|
|
48
|
+
- `vxil diff [--against <target>] [--ignore <k,…>] [--strict-secrets]` — the CI drift gate; secrets compared by name only, and a referenced-but-unstored name is informational unless `--strict-secrets`.
|
|
49
|
+
- `vxil doctor` — preflight: secrets referenced-vs-stored (advisory, names only) plus a served / published / local hash verdict per function. Under `--json` stdout is one JSON document; notices go to stderr.
|
|
50
|
+
- `vxil link <slug>` — reuses the key already stored for the slug on this machine; `--mint` for a fresh one, `--key` to hand one in.
|
|
51
|
+
- `vxil cms drop <collection> [--yes]` — remove one collection definition through the same guarded ack `push --allow-destructive` uses (rows are retained).
|
|
52
|
+
- `vxil api <METHOD> <path> ['<json>' | --data '<json>']` — call any `/v1` route; bare JSON on stdout, banner on stderr.
|
|
49
53
|
- `vxil secrets list --all-projects` — the names-and-metadata matrix across every bound project (values never come back).
|
|
50
54
|
- `vxil migrate payments --from-provider stripe|paddle|paypal|revenuecat (--customers <f.csv> | --all) [--dry-run]` — backfill subscriptions through the server sync leg; resumable.
|
|
51
55
|
- `vxil payments simulate --scenario <s> --user <id> [--tier <t>]` — mock/dev projects only: drive a refund pair, cross-platform unlock, renewal, expiry, past-due grace or transfer and check the oracle.
|
|
@@ -37,6 +37,12 @@ export declare function validateNotificationOverrides(templates: {
|
|
|
37
37
|
text?: string;
|
|
38
38
|
}>>;
|
|
39
39
|
} | undefined): string[];
|
|
40
|
+
/** AWS region grammar for `notifications.ses.region` (`us-east-1`,
|
|
41
|
+
* `eu-central-1`, `ap-southeast-2`, `us-gov-west-1`, …): two-letter partition,
|
|
42
|
+
* one or more lowercase words, a single digit. Pinned as a pattern rather than
|
|
43
|
+
* a list so a newly launched SES region needs no vxil release; a typo still
|
|
44
|
+
* fails at `vxil push` (TypeBox), never as a DNS error on the first send. */
|
|
45
|
+
export declare const SES_REGION_PATTERN = "^[a-z]{2}(-[a-z]+)+-\\d$";
|
|
40
46
|
export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObject<{
|
|
41
47
|
enabled: import("@sinclair/typebox").TBoolean;
|
|
42
48
|
fromEmail: import("@sinclair/typebox").TString;
|
|
@@ -44,7 +50,12 @@ export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObj
|
|
|
44
50
|
replyTo: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
45
51
|
resendApiKeyRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
46
52
|
webhookSecretRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
47
|
-
provider: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"resend">, import("@sinclair/typebox").TLiteral<"mock">]>;
|
|
53
|
+
provider: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"resend">, import("@sinclair/typebox").TLiteral<"ses">, import("@sinclair/typebox").TLiteral<"mock">]>;
|
|
54
|
+
ses: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
55
|
+
region: import("@sinclair/typebox").TString;
|
|
56
|
+
accessKeyIdRef: import("@sinclair/typebox").TString;
|
|
57
|
+
secretAccessKeyRef: import("@sinclair/typebox").TString;
|
|
58
|
+
}>>;
|
|
48
59
|
defaultLocale: import("@sinclair/typebox").TString;
|
|
49
60
|
retry: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
50
61
|
maxAttempts: import("@sinclair/typebox").TInteger;
|
|
@@ -53,10 +64,10 @@ export declare const NotificationsConfigSchema: import("@sinclair/typebox").TObj
|
|
|
53
64
|
suppression: import("@sinclair/typebox").TObject<{
|
|
54
65
|
softBounceThreshold: import("@sinclair/typebox").TInteger;
|
|
55
66
|
}>;
|
|
56
|
-
rateLimit: import("@sinclair/typebox").TObject<{
|
|
67
|
+
rateLimit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
57
68
|
perDay: import("@sinclair/typebox").TInteger;
|
|
58
69
|
perTenantSec: import("@sinclair/typebox").TInteger;
|
|
59
|
-
}
|
|
70
|
+
}>>;
|
|
60
71
|
templates: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
61
72
|
allowOverride: import("@sinclair/typebox").TBoolean;
|
|
62
73
|
overrides: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
|
|
@@ -150,6 +161,17 @@ declare const OidcProviderSchema: import("@sinclair/typebox").TObject<{
|
|
|
150
161
|
allowedDomains: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
151
162
|
autoLink: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
152
163
|
}>;
|
|
164
|
+
export declare const REDIRECT_ORIGIN_PATTERN = "^(https://[^/?#\\s]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]{1,5})?)$";
|
|
165
|
+
/** THE scheme rule for a redirect/return URL: https anywhere, http on loopback
|
|
166
|
+
* only. Takes the parsed URL (or any {protocol, hostname} pair) so the config
|
|
167
|
+
* validator and the worker's runtime guard call the identical predicate. */
|
|
168
|
+
export declare function redirectSchemeAllowed(u: {
|
|
169
|
+
protocol: string;
|
|
170
|
+
hostname: string;
|
|
171
|
+
}): boolean;
|
|
172
|
+
/** An allow-list ENTRY is a bare origin that parses and passes the scheme
|
|
173
|
+
* rule. Used by validateFeatureConfig (auth) on top of the coarse pattern. */
|
|
174
|
+
export declare function isAllowedRedirectOriginEntry(entry: string): boolean;
|
|
153
175
|
export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
|
|
154
176
|
enabled: import("@sinclair/typebox").TBoolean;
|
|
155
177
|
methods: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
package/dist/config.d.ts
CHANGED
|
@@ -120,15 +120,25 @@ export type FunctionTrigger = {
|
|
|
120
120
|
source: string;
|
|
121
121
|
}
|
|
122
122
|
/** `webhook`: the function is woken by a webhook_subscription fan-out whose
|
|
123
|
-
* target_url is `<edge>/v1/internal/fn/trigger/<name>?tenant=<id>`.
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* function
|
|
127
|
-
*
|
|
123
|
+
* target_url is `<edge>/v1/internal/fn/trigger/<name>?tenant=<id>`. That
|
|
124
|
+
* subscription is AUTO-WIRED on deploy (2026-09-19): the platform reconciles
|
|
125
|
+
* one per function declaring this binding, with `event_prefixes` = the
|
|
126
|
+
* function's `source` values — a hand-made one equal in target + prefixes is
|
|
127
|
+
* adopted. `source` is an event-name PREFIX (e.g. 'payments.') or a full event
|
|
128
|
+
* name — lowercase dotted segments, ≤64 chars, validated at deploy — and is
|
|
129
|
+
* also enforced at delivery: a non-matching event is ACK-200 skipped.
|
|
130
|
+
* Declaring the binding is what opts the function into event delivery; a
|
|
131
|
+
* receiver that declares only `cron` keeps the older cron+{} wake. */
|
|
128
132
|
| {
|
|
129
133
|
kind: 'webhook';
|
|
130
134
|
source: string;
|
|
131
|
-
}
|
|
135
|
+
}
|
|
136
|
+
/** `cmsHook`: fires after a CMS write in `collection`. The platform filters
|
|
137
|
+
* deliveries on the binding (2026-09-19): `beforeCreate` → created only,
|
|
138
|
+
* `beforeUpdate` → updated only, `beforeWrite` → created|updated; a write to
|
|
139
|
+
* another collection never wakes the function. Deleted/bulk events are not
|
|
140
|
+
* delivered to a cmsHook binding. */
|
|
141
|
+
| {
|
|
132
142
|
kind: 'cmsHook';
|
|
133
143
|
collection: string;
|
|
134
144
|
event: 'beforeCreate' | 'beforeUpdate' | 'beforeWrite';
|
|
@@ -211,6 +221,15 @@ export type ApiVersion = 'v1';
|
|
|
211
221
|
* only by a real v2. */
|
|
212
222
|
export declare const RELEASED_API_VERSIONS: readonly ApiVersion[];
|
|
213
223
|
/** The whole vxil backend, declared in one typed, version-controlled file. */
|
|
224
|
+
/** Recursive partial: a `vxil.config.ts` may write any SUBSET of a nested bag
|
|
225
|
+
* (`auth: { otp: { enabled: true } }`) — the server applies the defaults for
|
|
226
|
+
* the rest (Value.Default). A shallow `Partial` made `tsc` reject every
|
|
227
|
+
* partially-written nested bag, including the guide's own skeleton
|
|
228
|
+
* (found by the 2026-09-19 cvskit dry-run). Arrays and primitives are kept
|
|
229
|
+
* as-is; only plain object bags recurse. */
|
|
230
|
+
export type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
|
|
231
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
232
|
+
} : T;
|
|
214
233
|
export interface VxilConfig {
|
|
215
234
|
/** target environment label; resolved to a baseUrl by `vxil link` / `--env`. */
|
|
216
235
|
env?: string;
|
|
@@ -223,25 +242,25 @@ export interface VxilConfig {
|
|
|
223
242
|
* same Value.Default/Clean pipeline the server uses. Keys match FEATURE_SCHEMAS
|
|
224
243
|
* exactly (note the hyphenated ids). */
|
|
225
244
|
features?: {
|
|
226
|
-
notifications?:
|
|
227
|
-
jobs?:
|
|
228
|
-
auth?:
|
|
229
|
-
'rate-limits'?:
|
|
230
|
-
files?:
|
|
231
|
-
webhooks?:
|
|
232
|
-
comments?:
|
|
233
|
-
cms?:
|
|
234
|
-
mcp?:
|
|
235
|
-
realtime?:
|
|
236
|
-
presence?:
|
|
237
|
-
orgs?:
|
|
238
|
-
'activity-feed'?:
|
|
239
|
-
'vector-search'?:
|
|
240
|
-
ai?:
|
|
241
|
-
rag?:
|
|
242
|
-
payments?:
|
|
243
|
-
functions?:
|
|
244
|
-
copilot?:
|
|
245
|
+
notifications?: DeepPartial<NotificationsConfig>;
|
|
246
|
+
jobs?: DeepPartial<JobsConfig>;
|
|
247
|
+
auth?: DeepPartial<AuthConfig>;
|
|
248
|
+
'rate-limits'?: DeepPartial<RateLimitsConfig>;
|
|
249
|
+
files?: DeepPartial<FilesConfig>;
|
|
250
|
+
webhooks?: DeepPartial<WebhooksConfig>;
|
|
251
|
+
comments?: DeepPartial<CommentsConfig>;
|
|
252
|
+
cms?: DeepPartial<CmsConfig>;
|
|
253
|
+
mcp?: DeepPartial<McpConfig>;
|
|
254
|
+
realtime?: DeepPartial<RealtimeConfig>;
|
|
255
|
+
presence?: DeepPartial<PresenceConfig>;
|
|
256
|
+
orgs?: DeepPartial<OrgsConfig>;
|
|
257
|
+
'activity-feed'?: DeepPartial<ActivityFeedConfig>;
|
|
258
|
+
'vector-search'?: DeepPartial<VectorSearchConfig>;
|
|
259
|
+
ai?: DeepPartial<AiConfig>;
|
|
260
|
+
rag?: DeepPartial<RagConfig>;
|
|
261
|
+
payments?: DeepPartial<PaymentsConfig>;
|
|
262
|
+
functions?: DeepPartial<FunctionsConfig>;
|
|
263
|
+
copilot?: DeepPartial<CopilotConfig>;
|
|
245
264
|
};
|
|
246
265
|
/** (b) CMS SCHEMA-AS-CODE — collections + fields. `push` reconciles them
|
|
247
266
|
* additively against GET /v1/cms/collections (no destructive drops without
|