@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.
@@ -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. `inverse` names the reverse
91
- * traversal (parent → children, e.g. a note's `blocks`).
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` (TTS-1049), which named role KEYS. Role keys are
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 EID-409 home for segments. `sourceType: 'segment'`
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 (EID-46), e.g.
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 (EID-108) — the app's landing, not an OAuth
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 (EID-125). Roles are matched by `key` within the
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 (EID-17): the public `pk_` key can no longer grant
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 (EID-125) — without one, customers get read-only
769
- * tokens after the Model B auth change (EID-17).
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.65.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
  },