@foir/cli 0.65.0 → 0.67.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 +252 -88
- package/dist/lib/config-helpers.d.ts +77 -12
- package/dist/lib/config-helpers.js +4 -0
- package/package.json +2 -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';
|
|
@@ -234,7 +264,7 @@ interface ApplyConfigOperationInput {
|
|
|
234
264
|
* narrows who may execute. Omit or pass `[]` for no gate. Entries are verbs
|
|
235
265
|
* from the admin permission vocabulary, e.g. `['content:write']`.
|
|
236
266
|
*
|
|
237
|
-
* Replaced `allowedRoles
|
|
267
|
+
* Replaced `allowedRoles`, which named role KEYS. Role keys are
|
|
238
268
|
* yours to choose, so renaming a role silently changed who could execute.
|
|
239
269
|
*/
|
|
240
270
|
allowedPermissions?: string[];
|
|
@@ -359,7 +389,7 @@ interface ApplyConfigOperationInput {
|
|
|
359
389
|
responseTransform?: string;
|
|
360
390
|
}
|
|
361
391
|
/**
|
|
362
|
-
* A context dimension — the
|
|
392
|
+
* A context dimension — the home for segments. `sourceType: 'segment'`
|
|
363
393
|
* carries an `evaluationRules` RuleExpression (evaluated fresh at request time
|
|
364
394
|
* into segment.<key>); `sourceType: 'model'` designates a customer-context
|
|
365
395
|
* source model via `sourceModelKey`. Reconciled config-scoped by `foir push`
|
|
@@ -409,6 +439,34 @@ interface ApplyConfigVariantCatalogEntryInput {
|
|
|
409
439
|
isDefault?: boolean;
|
|
410
440
|
isActive?: boolean;
|
|
411
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
|
+
}
|
|
412
470
|
interface ApplyConfigScheduleInput {
|
|
413
471
|
/** Unique key for this schedule. Defaults to operationKey if not provided. */
|
|
414
472
|
key?: string;
|
|
@@ -487,7 +545,7 @@ interface ApplyConfigRelyingPartyInput {
|
|
|
487
545
|
*/
|
|
488
546
|
loginMethods?: ApplyConfigRelyingPartyLoginMethods;
|
|
489
547
|
/**
|
|
490
|
-
* White-label custom login domains for this RP
|
|
548
|
+
* White-label custom login domains for this RP, e.g.
|
|
491
549
|
* `['login.eide.clothing']`. `foir push` registers each (creating a pending
|
|
492
550
|
* row + a DNS-TXT challenge) and prunes any not listed. Verification is a
|
|
493
551
|
* separate step — add the CNAME + `_foir-challenge` TXT records the push
|
|
@@ -496,7 +554,7 @@ interface ApplyConfigRelyingPartyInput {
|
|
|
496
554
|
customDomains?: string[];
|
|
497
555
|
/**
|
|
498
556
|
* Where a bare custom-domain (front-door) login lands after Foir sets the
|
|
499
|
-
* first-party session cookie
|
|
557
|
+
* first-party session cookie — the app's landing, not an OAuth
|
|
500
558
|
* callback. Empty falls back to the redirect URI. Customer-lane only.
|
|
501
559
|
*/
|
|
502
560
|
postLoginUrl?: string;
|
|
@@ -560,13 +618,13 @@ interface ApplyConfigApiKeyInput {
|
|
|
560
618
|
/**
|
|
561
619
|
* A customer RBAC role declared in foir.config.ts and reconciled by
|
|
562
620
|
* `foir push`. The role's `permissions` become a customer access token's
|
|
563
|
-
* scope set at mint time
|
|
621
|
+
* scope set at mint time. Roles are matched by `key` within the
|
|
564
622
|
* project — a declared key that already exists is updated in place; a new
|
|
565
623
|
* key is created. (Roles present on the platform but absent from the config
|
|
566
624
|
* are left untouched — never auto-disabled.)
|
|
567
625
|
*
|
|
568
626
|
* Without a role, a consumer app's customers get read-only tokens after the
|
|
569
|
-
* Model B auth change
|
|
627
|
+
* Model B auth change: the public `pk_` key can no longer grant
|
|
570
628
|
* write/execute, so the only source of those scopes is the customer's role.
|
|
571
629
|
*/
|
|
572
630
|
interface ApplyConfigCustomerRoleInput {
|
|
@@ -765,8 +823,8 @@ interface ApplyConfigInput {
|
|
|
765
823
|
/**
|
|
766
824
|
* Customer RBAC roles, reconciled by `foir push`. Declare an
|
|
767
825
|
* `isDefault: true` role to grant every customer the write/execute scopes
|
|
768
|
-
* their tokens need
|
|
769
|
-
* 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.
|
|
770
828
|
*/
|
|
771
829
|
customerRoles?: ApplyConfigCustomerRoleInput[];
|
|
772
830
|
/** Per-project app declarations, keyed by app name. */
|
|
@@ -777,6 +835,11 @@ interface ApplyConfigInput {
|
|
|
777
835
|
* Add `--publish` to promote draft → published in the same push.
|
|
778
836
|
*/
|
|
779
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[];
|
|
780
843
|
[key: string]: unknown;
|
|
781
844
|
}
|
|
782
845
|
/** Define a complete config manifest. */
|
|
@@ -802,6 +865,8 @@ declare function defineOperation(operation: ApplyConfigOperationInput): ApplyCon
|
|
|
802
865
|
declare function defineContextDimension(dimension: ApplyConfigContextDimensionInput): ApplyConfigContextDimensionInput;
|
|
803
866
|
/** Define a project locale. */
|
|
804
867
|
declare function defineLocale(locale: ApplyConfigLocaleInput): ApplyConfigLocaleInput;
|
|
868
|
+
/** Define one transactional email's wording. */
|
|
869
|
+
declare function defineEmailCopy(copy: ApplyConfigEmailCopyInput): ApplyConfigEmailCopyInput;
|
|
805
870
|
/** Define a variant-catalog entry (targeting definition). */
|
|
806
871
|
declare function defineVariantCatalogEntry(entry: ApplyConfigVariantCatalogEntryInput): ApplyConfigVariantCatalogEntryInput;
|
|
807
872
|
/** Define a cron schedule for an operation. */
|
|
@@ -848,4 +913,4 @@ interface FoirSecretsConfig {
|
|
|
848
913
|
*/
|
|
849
914
|
declare function defineSecrets(config: FoirSecretsConfig): FoirSecretsConfig;
|
|
850
915
|
|
|
851
|
-
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.67.0",
|
|
4
4
|
"description": "Universal platform CLI for Foir platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"typescript": "5.9.2",
|
|
59
59
|
"vitest": "^3.2.4",
|
|
60
60
|
"@foir/assistant": "0.0.0",
|
|
61
|
-
"@foir/proto-ts": "0.109.0",
|
|
62
61
|
"@foir/console-routes": "0.0.0",
|
|
62
|
+
"@foir/proto-ts": "0.109.0",
|
|
63
63
|
"@foir/rpc-node": "0.0.0",
|
|
64
64
|
"@foir/sdk": "0.9.0"
|
|
65
65
|
},
|