@foir/cli 0.64.1 → 0.66.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/cli.js +1899 -448
- package/dist/lib/config-helpers.d.ts +91 -19
- package/dist/lib/config-helpers.js +4 -0
- package/package.json +4 -2
|
@@ -24,10 +24,21 @@ export { ExpressionPrecondition, Precondition, SegmentPrecondition } from '@foir
|
|
|
24
24
|
* For inline `{label, value}` choices, declare `type: 'enum'` instead — the
|
|
25
25
|
* `options?: never` here forbids the legacy shape at the type level.
|
|
26
26
|
*/
|
|
27
|
+
/**
|
|
28
|
+
* Where a field is edited, as distinct from what it holds.
|
|
29
|
+
*
|
|
30
|
+
* Most fields are content — what the record SAYS — and belong on the editor's
|
|
31
|
+
* main form. A field that says where the record sits or how it is handled
|
|
32
|
+
* reads as noise beside the prose, so `settings` moves it to the record's
|
|
33
|
+
* Settings tab instead. Presentation only: nothing about validation, storage
|
|
34
|
+
* or the published schema changes with it.
|
|
35
|
+
*/
|
|
36
|
+
type FieldPlacement = 'content' | 'settings';
|
|
27
37
|
interface SelectFieldConfig {
|
|
28
38
|
optionModelKey: string;
|
|
29
39
|
multiple?: boolean;
|
|
30
40
|
options?: never;
|
|
41
|
+
placement?: FieldPlacement;
|
|
31
42
|
}
|
|
32
43
|
interface EnumFieldOption {
|
|
33
44
|
label: string;
|
|
@@ -42,6 +53,7 @@ interface EnumFieldConfig {
|
|
|
42
53
|
options: EnumFieldOption[];
|
|
43
54
|
multiple?: boolean;
|
|
44
55
|
default?: string | string[];
|
|
56
|
+
placement?: FieldPlacement;
|
|
45
57
|
}
|
|
46
58
|
/**
|
|
47
59
|
* Per-field {read, write} principal allow-lists. A non-empty `write` names
|
|
@@ -63,6 +75,17 @@ interface BaseFieldDefinitionInput {
|
|
|
63
75
|
* where/orderBy on records. Without it the platform rejects sort by it.
|
|
64
76
|
*/
|
|
65
77
|
queryable?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Whether this field's text is embedded for AI search.
|
|
80
|
+
*
|
|
81
|
+
* Tri-state, and the three states are all different: omit to follow the
|
|
82
|
+
* model's own default for the field's type, `true` to always embed it, and
|
|
83
|
+
* `false` to never embed it. `false` is the one worth writing down — it is how
|
|
84
|
+
* a slug, an SKU or any other machine-shaped string is kept out of the
|
|
85
|
+
* semantic index, and leaving it to the admin means the next push cannot hold
|
|
86
|
+
* it.
|
|
87
|
+
*/
|
|
88
|
+
embed?: boolean;
|
|
66
89
|
helpText?: string;
|
|
67
90
|
placeholder?: string;
|
|
68
91
|
defaultValue?: unknown;
|
|
@@ -87,14 +110,20 @@ type EnumFieldDefinitionInput = BaseFieldDefinitionInput & {
|
|
|
87
110
|
* single-valued foreign key with ON DELETE CASCADE: sharing and deletes (and
|
|
88
111
|
* later ownership) cascade parent → child. Only valid on a single
|
|
89
112
|
* (non-`multiple`) reference. `association` (the default) is a plain reference;
|
|
90
|
-
* the target keeps its own access + lifecycle. `
|
|
91
|
-
*
|
|
113
|
+
* the target keeps its own access + lifecycle. `association_read` is an
|
|
114
|
+
* association that ALSO lets whoever can read the referenced record read the
|
|
115
|
+
* referring one — read-only, one hop. `hierarchy` makes the field the model's
|
|
116
|
+
* tree edge: foir answers `path` and `ancestors` by walking it, at most one per
|
|
117
|
+
* model and single-valued; its `referenceTypes` are unconstrained, since a
|
|
118
|
+
* parent need not share the child's model.
|
|
119
|
+
* `inverse` names the reverse traversal (parent → children, e.g. a note's
|
|
120
|
+
* `blocks`).
|
|
92
121
|
*
|
|
93
122
|
* The platform validates these at push (e.g. composition on a `multiple`
|
|
94
123
|
* reference is rejected).
|
|
95
124
|
*/
|
|
96
125
|
interface FieldRelationshipInput {
|
|
97
|
-
kind: 'composition' | 'association';
|
|
126
|
+
kind: 'composition' | 'association' | 'association_read' | 'hierarchy';
|
|
98
127
|
inverse?: string;
|
|
99
128
|
}
|
|
100
129
|
interface ReferenceFieldConfig {
|
|
@@ -103,6 +132,7 @@ interface ReferenceFieldConfig {
|
|
|
103
132
|
/** Many-valued reference. Composition is invalid when true. */
|
|
104
133
|
multiple?: boolean;
|
|
105
134
|
relationship?: FieldRelationshipInput;
|
|
135
|
+
placement?: FieldPlacement;
|
|
106
136
|
}
|
|
107
137
|
type ReferenceFieldDefinitionInput = BaseFieldDefinitionInput & {
|
|
108
138
|
type: 'reference';
|
|
@@ -154,13 +184,20 @@ interface AccessInput {
|
|
|
154
184
|
/**
|
|
155
185
|
* Visibility new records start at (the record descriptor modes):
|
|
156
186
|
* - `'private'` — only the owner and explicit grants.
|
|
157
|
-
* - `'public'` — readable by anyone
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
187
|
+
* - `'public'` — readable by anyone with API access, including signed-in
|
|
188
|
+
* customers on the public API.
|
|
189
|
+
*
|
|
190
|
+
* On a PROJECT-plane model, omitting this defaults to `'public'` at
|
|
191
|
+
* CREATION — project content is meant to be read, and `'private'` there does
|
|
192
|
+
* not mean what it looks like: a project record has no owner, so it stays
|
|
193
|
+
* readable by anonymous and server-side callers while being hidden from
|
|
194
|
+
* signed-in customers. Set `'private'` explicitly to opt a project model out.
|
|
195
|
+
*
|
|
196
|
+
* The default is applied by the platform, at the create path only. Omitting it
|
|
197
|
+
* on a model that already exists means "leave it alone" — it will not reset a
|
|
198
|
+
* visibility you changed in the console. Owned planes (customer/admin) are
|
|
199
|
+
* always private (the platform rejects a public default there) regardless of
|
|
200
|
+
* this value.
|
|
164
201
|
*/
|
|
165
202
|
defaultVisibility?: 'private' | 'public';
|
|
166
203
|
}
|
|
@@ -227,7 +264,7 @@ interface ApplyConfigOperationInput {
|
|
|
227
264
|
* narrows who may execute. Omit or pass `[]` for no gate. Entries are verbs
|
|
228
265
|
* from the admin permission vocabulary, e.g. `['content:write']`.
|
|
229
266
|
*
|
|
230
|
-
* Replaced `allowedRoles
|
|
267
|
+
* Replaced `allowedRoles`, which named role KEYS. Role keys are
|
|
231
268
|
* yours to choose, so renaming a role silently changed who could execute.
|
|
232
269
|
*/
|
|
233
270
|
allowedPermissions?: string[];
|
|
@@ -352,7 +389,7 @@ interface ApplyConfigOperationInput {
|
|
|
352
389
|
responseTransform?: string;
|
|
353
390
|
}
|
|
354
391
|
/**
|
|
355
|
-
* A context dimension — the
|
|
392
|
+
* A context dimension — the home for segments. `sourceType: 'segment'`
|
|
356
393
|
* carries an `evaluationRules` RuleExpression (evaluated fresh at request time
|
|
357
394
|
* into segment.<key>); `sourceType: 'model'` designates a customer-context
|
|
358
395
|
* source model via `sourceModelKey`. Reconciled config-scoped by `foir push`
|
|
@@ -402,6 +439,34 @@ interface ApplyConfigVariantCatalogEntryInput {
|
|
|
402
439
|
isDefault?: boolean;
|
|
403
440
|
isActive?: boolean;
|
|
404
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* One transactional email's wording, keyed by the action that sends it.
|
|
444
|
+
*
|
|
445
|
+
* `actionKey` names a send path — `email.customer.welcome`, or a notification
|
|
446
|
+
* hook's own key. The platform refuses one it does not recognise, because a row
|
|
447
|
+
* written under a typo is read by nothing and looks applied.
|
|
448
|
+
*
|
|
449
|
+
* Reconciled by `foir push`, create/update only: a `workspace` override is
|
|
450
|
+
* tenant-level and inherited by every project, so two projects in one workspace
|
|
451
|
+
* can name the same row and an omission must not delete what the other declares.
|
|
452
|
+
* Remove an override in the console.
|
|
453
|
+
*/
|
|
454
|
+
interface ApplyConfigEmailCopyInput {
|
|
455
|
+
/** The send path this wording belongs to. */
|
|
456
|
+
actionKey: string;
|
|
457
|
+
/** BCP-47 locale. Defaults to `en`. */
|
|
458
|
+
locale?: string;
|
|
459
|
+
/** `project` (default) or `workspace`, which every project inherits. */
|
|
460
|
+
scope?: 'project' | 'workspace';
|
|
461
|
+
subject?: string;
|
|
462
|
+
heading?: string;
|
|
463
|
+
body?: string;
|
|
464
|
+
footer?: string;
|
|
465
|
+
ctas?: Array<{
|
|
466
|
+
label: string;
|
|
467
|
+
url: string;
|
|
468
|
+
}>;
|
|
469
|
+
}
|
|
405
470
|
interface ApplyConfigScheduleInput {
|
|
406
471
|
/** Unique key for this schedule. Defaults to operationKey if not provided. */
|
|
407
472
|
key?: string;
|
|
@@ -480,7 +545,7 @@ interface ApplyConfigRelyingPartyInput {
|
|
|
480
545
|
*/
|
|
481
546
|
loginMethods?: ApplyConfigRelyingPartyLoginMethods;
|
|
482
547
|
/**
|
|
483
|
-
* White-label custom login domains for this RP
|
|
548
|
+
* White-label custom login domains for this RP, e.g.
|
|
484
549
|
* `['login.eide.clothing']`. `foir push` registers each (creating a pending
|
|
485
550
|
* row + a DNS-TXT challenge) and prunes any not listed. Verification is a
|
|
486
551
|
* separate step — add the CNAME + `_foir-challenge` TXT records the push
|
|
@@ -489,7 +554,7 @@ interface ApplyConfigRelyingPartyInput {
|
|
|
489
554
|
customDomains?: string[];
|
|
490
555
|
/**
|
|
491
556
|
* Where a bare custom-domain (front-door) login lands after Foir sets the
|
|
492
|
-
* first-party session cookie
|
|
557
|
+
* first-party session cookie — the app's landing, not an OAuth
|
|
493
558
|
* callback. Empty falls back to the redirect URI. Customer-lane only.
|
|
494
559
|
*/
|
|
495
560
|
postLoginUrl?: string;
|
|
@@ -553,13 +618,13 @@ interface ApplyConfigApiKeyInput {
|
|
|
553
618
|
/**
|
|
554
619
|
* A customer RBAC role declared in foir.config.ts and reconciled by
|
|
555
620
|
* `foir push`. The role's `permissions` become a customer access token's
|
|
556
|
-
* scope set at mint time
|
|
621
|
+
* scope set at mint time. Roles are matched by `key` within the
|
|
557
622
|
* project — a declared key that already exists is updated in place; a new
|
|
558
623
|
* key is created. (Roles present on the platform but absent from the config
|
|
559
624
|
* are left untouched — never auto-disabled.)
|
|
560
625
|
*
|
|
561
626
|
* Without a role, a consumer app's customers get read-only tokens after the
|
|
562
|
-
* Model B auth change
|
|
627
|
+
* Model B auth change: the public `pk_` key can no longer grant
|
|
563
628
|
* write/execute, so the only source of those scopes is the customer's role.
|
|
564
629
|
*/
|
|
565
630
|
interface ApplyConfigCustomerRoleInput {
|
|
@@ -758,8 +823,8 @@ interface ApplyConfigInput {
|
|
|
758
823
|
/**
|
|
759
824
|
* Customer RBAC roles, reconciled by `foir push`. Declare an
|
|
760
825
|
* `isDefault: true` role to grant every customer the write/execute scopes
|
|
761
|
-
* their tokens need
|
|
762
|
-
* tokens after the Model B auth change
|
|
826
|
+
* their tokens need — without one, customers get read-only
|
|
827
|
+
* tokens after the Model B auth change.
|
|
763
828
|
*/
|
|
764
829
|
customerRoles?: ApplyConfigCustomerRoleInput[];
|
|
765
830
|
/** Per-project app declarations, keyed by app name. */
|
|
@@ -770,6 +835,11 @@ interface ApplyConfigInput {
|
|
|
770
835
|
* Add `--publish` to promote draft → published in the same push.
|
|
771
836
|
*/
|
|
772
837
|
designTokens?: ApplyConfigDesignTokensInput;
|
|
838
|
+
/**
|
|
839
|
+
* Transactional email wording, reconciled by `foir push` (create/update
|
|
840
|
+
* only — see ApplyConfigEmailCopyInput for why it never prunes).
|
|
841
|
+
*/
|
|
842
|
+
emailCopy?: ApplyConfigEmailCopyInput[];
|
|
773
843
|
[key: string]: unknown;
|
|
774
844
|
}
|
|
775
845
|
/** Define a complete config manifest. */
|
|
@@ -795,6 +865,8 @@ declare function defineOperation(operation: ApplyConfigOperationInput): ApplyCon
|
|
|
795
865
|
declare function defineContextDimension(dimension: ApplyConfigContextDimensionInput): ApplyConfigContextDimensionInput;
|
|
796
866
|
/** Define a project locale. */
|
|
797
867
|
declare function defineLocale(locale: ApplyConfigLocaleInput): ApplyConfigLocaleInput;
|
|
868
|
+
/** Define one transactional email's wording. */
|
|
869
|
+
declare function defineEmailCopy(copy: ApplyConfigEmailCopyInput): ApplyConfigEmailCopyInput;
|
|
798
870
|
/** Define a variant-catalog entry (targeting definition). */
|
|
799
871
|
declare function defineVariantCatalogEntry(entry: ApplyConfigVariantCatalogEntryInput): ApplyConfigVariantCatalogEntryInput;
|
|
800
872
|
/** Define a cron schedule for an operation. */
|
|
@@ -841,4 +913,4 @@ interface FoirSecretsConfig {
|
|
|
841
913
|
*/
|
|
842
914
|
declare function defineSecrets(config: FoirSecretsConfig): FoirSecretsConfig;
|
|
843
915
|
|
|
844
|
-
export { type AccessInput, type AppInput, type AppPlacementFieldChoiceInput, type AppSinkMappingInput, type AppSourceMappingInput, type ApplyConfigApiKeyInput, type ApplyConfigAuthProviderInput, type ApplyConfigContextDimensionInput, type ApplyConfigCustomerRoleInput, type ApplyConfigDesignTokensInput, type ApplyConfigHookInput, type ApplyConfigInput, type ApplyConfigLocaleInput, type ApplyConfigModelInput, type ApplyConfigOperationInput, type ApplyConfigPlacementInput, type ApplyConfigProjectInput, type ApplyConfigProjectSettingsInput, type ApplyConfigRelyingPartyInput, type ApplyConfigRelyingPartyLoginMethods, type ApplyConfigScheduleInput, type ApplyConfigVariantCatalogEntryInput, type EnumFieldConfig, type EnumFieldDefinitionInput, type EnumFieldOption, type FieldAccessInput, type FieldDefinitionInput, type FieldRelationshipInput, type FoirSecretsConfig, type LookupDefinitionInput, type QuotaRule, type ReferenceFieldConfig, type ReferenceFieldDefinitionInput, type SecretDeclaration, type SecretOwnerKind, type SelectFieldConfig, type SelectFieldDefinitionInput, type SignupModeName, type SignupRestrictionsInput, defineAuthProvider, defineConfig, defineContextDimension, defineDesignTokens, defineEnumField, defineField, defineHook, defineLocale, defineModel, defineOperation, definePlacement, defineRelyingParty, defineSchedule, defineSecrets, defineSelectField, defineVariantCatalogEntry };
|
|
916
|
+
export { type AccessInput, type AppInput, type AppPlacementFieldChoiceInput, type AppSinkMappingInput, type AppSourceMappingInput, type ApplyConfigApiKeyInput, type ApplyConfigAuthProviderInput, type ApplyConfigContextDimensionInput, type ApplyConfigCustomerRoleInput, type ApplyConfigDesignTokensInput, type ApplyConfigEmailCopyInput, type ApplyConfigHookInput, type ApplyConfigInput, type ApplyConfigLocaleInput, type ApplyConfigModelInput, type ApplyConfigOperationInput, type ApplyConfigPlacementInput, type ApplyConfigProjectInput, type ApplyConfigProjectSettingsInput, type ApplyConfigRelyingPartyInput, type ApplyConfigRelyingPartyLoginMethods, type ApplyConfigScheduleInput, type ApplyConfigVariantCatalogEntryInput, type EnumFieldConfig, type EnumFieldDefinitionInput, type EnumFieldOption, type FieldAccessInput, type FieldDefinitionInput, type FieldPlacement, type FieldRelationshipInput, type FoirSecretsConfig, type LookupDefinitionInput, type QuotaRule, type ReferenceFieldConfig, type ReferenceFieldDefinitionInput, type SecretDeclaration, type SecretOwnerKind, type SelectFieldConfig, type SelectFieldDefinitionInput, type SignupModeName, type SignupRestrictionsInput, defineAuthProvider, defineConfig, defineContextDimension, defineDesignTokens, defineEmailCopy, defineEnumField, defineField, defineHook, defineLocale, defineModel, defineOperation, definePlacement, defineRelyingParty, defineSchedule, defineSecrets, defineSelectField, defineVariantCatalogEntry };
|
|
@@ -26,6 +26,9 @@ function defineContextDimension(dimension) {
|
|
|
26
26
|
function defineLocale(locale) {
|
|
27
27
|
return locale;
|
|
28
28
|
}
|
|
29
|
+
function defineEmailCopy(copy) {
|
|
30
|
+
return copy;
|
|
31
|
+
}
|
|
29
32
|
function defineVariantCatalogEntry(entry) {
|
|
30
33
|
return entry;
|
|
31
34
|
}
|
|
@@ -52,6 +55,7 @@ export {
|
|
|
52
55
|
defineConfig,
|
|
53
56
|
defineContextDimension,
|
|
54
57
|
defineDesignTokens,
|
|
58
|
+
defineEmailCopy,
|
|
55
59
|
defineEnumField,
|
|
56
60
|
defineField,
|
|
57
61
|
defineHook,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foir/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.0",
|
|
4
4
|
"description": "Universal platform CLI for Foir platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"@connectrpc/connect": "^2.0.0",
|
|
39
39
|
"@connectrpc/connect-node": "^2.0.0",
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
41
|
+
"ai": "^7.0.29",
|
|
41
42
|
"chalk": "^5.3.0",
|
|
42
43
|
"commander": "^12.1.0",
|
|
43
44
|
"dotenv": "^16.4.5",
|
|
@@ -56,10 +57,11 @@
|
|
|
56
57
|
"tsx": "^4.20.0",
|
|
57
58
|
"typescript": "5.9.2",
|
|
58
59
|
"vitest": "^3.2.4",
|
|
60
|
+
"@foir/assistant": "0.0.0",
|
|
59
61
|
"@foir/console-routes": "0.0.0",
|
|
60
62
|
"@foir/proto-ts": "0.109.0",
|
|
61
63
|
"@foir/rpc-node": "0.0.0",
|
|
62
|
-
"@foir/sdk": "0.
|
|
64
|
+
"@foir/sdk": "0.9.0"
|
|
63
65
|
},
|
|
64
66
|
"engines": {
|
|
65
67
|
"node": ">=18.0.0"
|