@foir/cli 0.71.0 → 0.72.1
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 +5782 -4322
- package/dist/lib/config-helpers.d.ts +144 -28
- package/dist/lib/migrations.d.ts +22 -4
- package/package.json +2 -2
|
@@ -129,6 +129,17 @@ interface FieldRelationshipInput {
|
|
|
129
129
|
interface ReferenceFieldConfig {
|
|
130
130
|
/** Models this reference may target. Empty ⇒ any record-bearing model. */
|
|
131
131
|
referenceTypes?: string[];
|
|
132
|
+
/**
|
|
133
|
+
* The name of the GraphQL union a reference to two or more models returns.
|
|
134
|
+
* Omitted, the union is named from its models (`page` and `product` give
|
|
135
|
+
* `PageProductRef`), which moves when the models do and which a model key can
|
|
136
|
+
* land on. A name given here stays put.
|
|
137
|
+
*
|
|
138
|
+
* Only for two or more `referenceTypes`: a reference to one model returns that
|
|
139
|
+
* model's own type, and the platform refuses a name with nothing to name. Two
|
|
140
|
+
* fields may give one name only over the same models.
|
|
141
|
+
*/
|
|
142
|
+
unionName?: string;
|
|
132
143
|
/** Many-valued reference. Composition is invalid when true. */
|
|
133
144
|
multiple?: boolean;
|
|
134
145
|
relationship?: FieldRelationshipInput;
|
|
@@ -163,11 +174,12 @@ interface LookupDefinitionInput {
|
|
|
163
174
|
name?: string;
|
|
164
175
|
}
|
|
165
176
|
/**
|
|
166
|
-
* Per-model access policy
|
|
177
|
+
* Per-model access policy: the unified resource-access primitive's BROAD
|
|
167
178
|
* layer. Mirrors the admin UI's model "Owner" plane control and the platform
|
|
168
179
|
* `models.v1.AccessConfig`. Declared here, it is reconciled into the model's
|
|
169
|
-
* config by `foir push
|
|
170
|
-
*
|
|
180
|
+
* config by `foir push`, and takes effect for the published channel when the
|
|
181
|
+
* model is published; omit `access` entirely for a project-owned model, whose
|
|
182
|
+
* default visibility follows the rule on `defaultVisibility` below.
|
|
171
183
|
*/
|
|
172
184
|
interface AccessInput {
|
|
173
185
|
/**
|
|
@@ -184,7 +196,7 @@ interface AccessInput {
|
|
|
184
196
|
* the row stays in the project plane, so the project credential finds
|
|
185
197
|
* every organization's rows — which is how a multi-tenant app resolves
|
|
186
198
|
* `<sub>.theapp.com` to an org before it knows which org the request is
|
|
187
|
-
* for. Written by the project (never
|
|
199
|
+
* for. Written by the project (never for a customer), owner
|
|
188
200
|
* named by `ownerId` or by the org the caller acts in (`X-Org-Id`);
|
|
189
201
|
* `singleton` means one record per organization. Refused on a project
|
|
190
202
|
* with no organizations and on a shared (layered/group) model.
|
|
@@ -294,21 +306,20 @@ interface ApplyConfigOperationInput {
|
|
|
294
306
|
/** Precondition that must be met before execution (e.g., segment membership). */
|
|
295
307
|
precondition?: Precondition;
|
|
296
308
|
/**
|
|
297
|
-
* Execution mode the operation supports. `sync`
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* mutation when work completes. Use
|
|
302
|
-
*
|
|
309
|
+
* Execution mode the operation supports. `sync` is a direct call: the
|
|
310
|
+
* platform calls the endpoint and returns its answer to the caller, bounded
|
|
311
|
+
* by the operation's `timeoutMs`, and the answer is the result. `async`
|
|
312
|
+
* dispatches with 202-ack semantics: the extension returns 202 immediately
|
|
313
|
+
* and calls back via the typed GraphQL mutation when work completes. Use
|
|
314
|
+
* `async` for work nobody waits on, or that runs longer than a caller can.
|
|
303
315
|
*
|
|
304
316
|
* There is only one flavour of async on the platform: the callback-based
|
|
305
|
-
* handshake.
|
|
306
|
-
*
|
|
307
|
-
* and the platform tracks the execution to terminal state.
|
|
317
|
+
* handshake. Every ASYNC dispatch carries a scoped callback token and the
|
|
318
|
+
* platform tracks the execution to terminal state.
|
|
308
319
|
*
|
|
309
|
-
* Default: `sync`. The CLI maps this to `supportsAsync` on the
|
|
310
|
-
*
|
|
311
|
-
*
|
|
320
|
+
* Default: `sync`. The CLI maps this to `supportsAsync` on the platform
|
|
321
|
+
* Operation row; the operation can still be called in SYNC mode at runtime
|
|
322
|
+
* when callers want a blocking call.
|
|
312
323
|
*/
|
|
313
324
|
mode?: 'sync' | 'async';
|
|
314
325
|
/**
|
|
@@ -344,9 +355,17 @@ interface ApplyConfigOperationInput {
|
|
|
344
355
|
* touches; the unscoped two-part forms (`records:read` etc.) only
|
|
345
356
|
* work for admin API keys and won't satisfy customer-scoped tokens.
|
|
346
357
|
*
|
|
358
|
+
* Ownership, `records:attribute:<model_key>` (or `records:attribute` for
|
|
359
|
+
* every model), lets the operation name the customer a record belongs to
|
|
360
|
+
* when it creates one (`create<Model>(input, ownerId)`) and hand a record
|
|
361
|
+
* to another customer (`set<Model>Owner`). It is held only by an
|
|
362
|
+
* operation: no API key of any type can be minted with it.
|
|
363
|
+
*
|
|
347
364
|
* Cross-cutting caps: `embeddings:read`, `embeddings:write`,
|
|
348
|
-
* `files:read`, `files:write`,
|
|
349
|
-
*
|
|
365
|
+
* `files:read`, `files:write`, `customers:write` (create a customer,
|
|
366
|
+
* such as the buyer at a guest checkout),
|
|
367
|
+
* `config:read`, `status:write`, and the secrets:* family. A capability
|
|
368
|
+
* outside this vocabulary is refused at push, by name. See
|
|
350
369
|
* `services/internal/scopedtoken/capabilities.go` for the full enum.
|
|
351
370
|
*
|
|
352
371
|
* `operations:complete` is auto-injected on callback tokens — never
|
|
@@ -570,7 +589,12 @@ interface ApplyConfigRelyingPartyInput {
|
|
|
570
589
|
* userinfo.
|
|
571
590
|
*/
|
|
572
591
|
redirectUris: string[];
|
|
573
|
-
/**
|
|
592
|
+
/**
|
|
593
|
+
* OAuth scopes the RP can request. A request is granted the scopes this list
|
|
594
|
+
* covers, narrowed forms included: `operations:execute` here grants a request
|
|
595
|
+
* for `operations:execute:checkout`. A scope wider than anything listed is
|
|
596
|
+
* not granted.
|
|
597
|
+
*/
|
|
574
598
|
allowedScopes?: string[];
|
|
575
599
|
/**
|
|
576
600
|
* Per-RP login method filter (customer-lane). Combines with the project-
|
|
@@ -636,9 +660,9 @@ interface ApplyConfigHookInput {
|
|
|
636
660
|
isActive?: boolean;
|
|
637
661
|
}
|
|
638
662
|
interface ApplyConfigApiKeyInput {
|
|
639
|
-
/** Name for this API key (e.g. "Tilly iOS", "Tilly
|
|
663
|
+
/** Name for this API key (e.g. "Tilly iOS", "Tilly CRM sync"). */
|
|
640
664
|
name: string;
|
|
641
|
-
/** Key type: 'public' for
|
|
665
|
+
/** Key type: 'public' for your app, 'secret' for a third-party client's own server. */
|
|
642
666
|
keyType: 'public' | 'secret';
|
|
643
667
|
/** Environment variable name to write the key to in .env (e.g. "FOIR_PUBLIC_KEY"). */
|
|
644
668
|
envVar: string;
|
|
@@ -667,6 +691,20 @@ interface ApplyConfigApiKeyInput {
|
|
|
667
691
|
* credential to change which tenant it belongs to.
|
|
668
692
|
*/
|
|
669
693
|
org?: string;
|
|
694
|
+
/**
|
|
695
|
+
* What a stranger may do with this key: the caller carrying it with nobody
|
|
696
|
+
* signed in, such as a guest at checkout or a contact form.
|
|
697
|
+
*
|
|
698
|
+
* Two forms, each naming one thing this config declares:
|
|
699
|
+
* `records:create:<model>` and `operations:execute:<operation>`. No wildcard,
|
|
700
|
+
* no update, delete or publish. A PUBLIC key only: a secret key is presented
|
|
701
|
+
* by a server that already knows who is calling.
|
|
702
|
+
*
|
|
703
|
+
* A push changes it on the existing key in place, as it does the key's
|
|
704
|
+
* scopes: the key keeps its id and its secret, and the new list is live the
|
|
705
|
+
* moment the push writes it, not at a release.
|
|
706
|
+
*/
|
|
707
|
+
anonymousScopes?: string[];
|
|
670
708
|
}
|
|
671
709
|
/**
|
|
672
710
|
* A customer RBAC role declared in foir.config.ts and reconciled by
|
|
@@ -692,6 +730,12 @@ interface ApplyConfigCustomerRoleInput {
|
|
|
692
730
|
* records:{read,write}:<model> for every customer-writable model in the
|
|
693
731
|
* project — but does NOT include operations:execute, so list that
|
|
694
732
|
* explicitly when the app's customers run operations.
|
|
733
|
+
*
|
|
734
|
+
* `operations:execute` runs every operation the key exposes;
|
|
735
|
+
* `operations:execute:<operation_key>` runs that one alone, so a role can
|
|
736
|
+
* let every customer check out and keep rebooking for the customers whose
|
|
737
|
+
* role grants it. A push
|
|
738
|
+
* naming an operation the project does not have is refused, by name.
|
|
695
739
|
*/
|
|
696
740
|
permissions: string[];
|
|
697
741
|
/**
|
|
@@ -764,10 +808,10 @@ interface AppInput {
|
|
|
764
808
|
};
|
|
765
809
|
}
|
|
766
810
|
/**
|
|
767
|
-
* Project-level settings applied
|
|
768
|
-
* read via GetProject at pull time.
|
|
769
|
-
* clear on the platform side: `''` clears
|
|
770
|
-
* it untouched.
|
|
811
|
+
* Project-level settings applied by `foir push`, in the same server-side
|
|
812
|
+
* transaction as every other family, and read via GetProject at pull time.
|
|
813
|
+
* Branding fields are empty-string-as-clear on the platform side: `''` clears
|
|
814
|
+
* the column, `undefined` leaves it untouched.
|
|
771
815
|
*/
|
|
772
816
|
interface ApplyConfigProjectSettingsInput {
|
|
773
817
|
/** Display name in customer-facing emails / portal headers. */
|
|
@@ -863,7 +907,7 @@ interface SignupRestrictionsInput {
|
|
|
863
907
|
/**
|
|
864
908
|
* Project pin + declarative settings. The pin (id / tenantId) is a guard
|
|
865
909
|
* against pushing one folder's config into another folder's project; the
|
|
866
|
-
* settings are applied
|
|
910
|
+
* settings are applied by the push, on the server, with the rest of the config.
|
|
867
911
|
*/
|
|
868
912
|
interface ApplyConfigProjectInput {
|
|
869
913
|
/** Project id this folder is bound to. */
|
|
@@ -1017,8 +1061,80 @@ interface ApplyConfigProjectEmailInput {
|
|
|
1017
1061
|
/** A vault ref, never a key. `foir secrets put` mints one. */
|
|
1018
1062
|
resendKeySecretRef?: string;
|
|
1019
1063
|
}
|
|
1020
|
-
/**
|
|
1064
|
+
/**
|
|
1065
|
+
* One value a named project supplies for a destination: a URL, an address, a
|
|
1066
|
+
* list of them, or an app's settings.
|
|
1067
|
+
*/
|
|
1068
|
+
type DestinationValue = string | string[] | Record<string, unknown>;
|
|
1069
|
+
/** A named project's destinations, under the names the manifest reads them by. */
|
|
1070
|
+
type DestinationValues = Record<string, DestinationValue>;
|
|
1071
|
+
/**
|
|
1072
|
+
* One named project: an ordinary project this config pushes to when selected
|
|
1073
|
+
* with `--project <name>` or `FOIR_PROJECT=<name>`.
|
|
1074
|
+
*
|
|
1075
|
+
* The platform never learns that two named projects are related. What makes
|
|
1076
|
+
* them one config is that the CLI resolves the manifest once per project and
|
|
1077
|
+
* refuses a push when any two differ in a field that is not a destination.
|
|
1078
|
+
*/
|
|
1079
|
+
interface NamedProjectInput<D extends DestinationValues = DestinationValues> {
|
|
1080
|
+
/** The project's id. */
|
|
1081
|
+
id: string;
|
|
1082
|
+
/** The id of the project's tenant. */
|
|
1083
|
+
tenantId: string;
|
|
1084
|
+
/** A human-friendly name for output. */
|
|
1085
|
+
name?: string;
|
|
1086
|
+
/**
|
|
1087
|
+
* The file this project's minted keys and signing secrets are written to,
|
|
1088
|
+
* relative to the config. Two named projects may not share one.
|
|
1089
|
+
*/
|
|
1090
|
+
envFile: string;
|
|
1091
|
+
/**
|
|
1092
|
+
* The control plane this project lives on. Omit for the production control
|
|
1093
|
+
* plane. A command pointed anywhere else, or holding a session minted
|
|
1094
|
+
* anywhere else, is refused unless it passes `--allow-control-plane-mismatch`.
|
|
1095
|
+
*/
|
|
1096
|
+
apiUrl?: string;
|
|
1097
|
+
/**
|
|
1098
|
+
* The values that may differ between named projects: endpoints, redirect
|
|
1099
|
+
* URIs, origins, sender addresses and the like. The manifest reads them from
|
|
1100
|
+
* the project it is resolved for. Every named project supplies the same
|
|
1101
|
+
* names.
|
|
1102
|
+
*/
|
|
1103
|
+
destinations: D;
|
|
1104
|
+
}
|
|
1105
|
+
/** What the manifest function is given: the project it is resolved for. */
|
|
1106
|
+
interface SelectedNamedProject<D extends DestinationValues = DestinationValues> {
|
|
1107
|
+
/** The name this project is declared under in `projects`. */
|
|
1108
|
+
name: string;
|
|
1109
|
+
destinations: D;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* The manifest a named-projects config resolves to, for one project.
|
|
1113
|
+
*
|
|
1114
|
+
* The same shape as a single-project manifest, less the pin: a named project's
|
|
1115
|
+
* `id`, `tenantId` and `name` come from its entry in `projects`, so `project`
|
|
1116
|
+
* here carries only its `settings`.
|
|
1117
|
+
*/
|
|
1118
|
+
interface NamedProjectManifest extends Omit<ApplyConfigInput, 'project'> {
|
|
1119
|
+
project?: {
|
|
1120
|
+
settings?: ApplyConfigProjectSettingsInput;
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* A config that declares several full projects by name.
|
|
1125
|
+
*
|
|
1126
|
+
* `manifest` is called once per declared project, with that project's
|
|
1127
|
+
* destinations. Only destinations may differ between the results: a push
|
|
1128
|
+
* refuses, before anything is sent, when two resolved manifests differ in any
|
|
1129
|
+
* other field, and names the field and both values.
|
|
1130
|
+
*/
|
|
1131
|
+
interface FoirProjectsConfig<D extends DestinationValues = DestinationValues> {
|
|
1132
|
+
projects: Record<string, NamedProjectInput<D>>;
|
|
1133
|
+
manifest: (project: SelectedNamedProject<D>) => NamedProjectManifest | Promise<NamedProjectManifest>;
|
|
1134
|
+
}
|
|
1135
|
+
/** Define a complete config manifest, or several named projects built from one. */
|
|
1021
1136
|
declare function defineConfig(config: ApplyConfigInput): ApplyConfigInput;
|
|
1137
|
+
declare function defineConfig<D extends DestinationValues>(config: FoirProjectsConfig<D>): FoirProjectsConfig<D>;
|
|
1022
1138
|
/** Define a model with type-safe field definitions. */
|
|
1023
1139
|
declare function defineModel(model: ApplyConfigModelInput): ApplyConfigModelInput;
|
|
1024
1140
|
/** Define a field with type safety. */
|
|
@@ -1088,4 +1204,4 @@ interface FoirSecretsConfig {
|
|
|
1088
1204
|
*/
|
|
1089
1205
|
declare function defineSecrets(config: FoirSecretsConfig): FoirSecretsConfig;
|
|
1090
1206
|
|
|
1091
|
-
export { type AccessInput, type AppInput, type AppMappingDeclined, 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 ApplyConfigProjectEmailInput, 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 };
|
|
1207
|
+
export { type AccessInput, type AppInput, type AppMappingDeclined, 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 ApplyConfigProjectEmailInput, type ApplyConfigProjectInput, type ApplyConfigProjectSettingsInput, type ApplyConfigRelyingPartyInput, type ApplyConfigRelyingPartyLoginMethods, type ApplyConfigScheduleInput, type ApplyConfigVariantCatalogEntryInput, type DestinationValue, type DestinationValues, type EnumFieldConfig, type EnumFieldDefinitionInput, type EnumFieldOption, type FieldAccessInput, type FieldDefinitionInput, type FieldPlacement, type FieldRelationshipInput, type FoirProjectsConfig, type FoirSecretsConfig, type LookupDefinitionInput, type NamedProjectInput, type NamedProjectManifest, type QuotaRule, type ReferenceFieldConfig, type ReferenceFieldDefinitionInput, type SecretDeclaration, type SecretOwnerKind, type SelectFieldConfig, type SelectFieldDefinitionInput, type SelectedNamedProject, type SignupModeName, type SignupRestrictionsInput, defineAuthProvider, defineConfig, defineContextDimension, defineDesignTokens, defineEmailCopy, defineEnumField, defineField, defineHook, defineLocale, defineModel, defineOperation, definePlacement, defineRelyingParty, defineSchedule, defineSecrets, defineSelectField, defineVariantCatalogEntry };
|
package/dist/lib/migrations.d.ts
CHANGED
|
@@ -71,8 +71,25 @@ type Hold = {
|
|
|
71
71
|
hold: string;
|
|
72
72
|
};
|
|
73
73
|
type FieldDecision = RenameTo | Retype | RemapValues | SplitInto | JoinFrom | FillFrom | Drop | Hold;
|
|
74
|
-
/**
|
|
75
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Who owns the records that name no owner when the model moves onto an owned
|
|
76
|
+
* plane: each takes the owner one of its fields names, or they are left as
|
|
77
|
+
* they are while you handle it. A release carrying the move refuses until this
|
|
78
|
+
* is answered, and an answered assignment runs inside that release.
|
|
79
|
+
*/
|
|
80
|
+
type OwnerDecision = {
|
|
81
|
+
assignFrom: string;
|
|
82
|
+
} | {
|
|
83
|
+
hold: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* One model's decisions, keyed by the field being removed, and under `$owner`
|
|
87
|
+
* the decision about records that name no owner.
|
|
88
|
+
*/
|
|
89
|
+
type ModelDecisions = {
|
|
90
|
+
[fieldKey: string]: FieldDecision | OwnerDecision | undefined;
|
|
91
|
+
$owner?: OwnerDecision;
|
|
92
|
+
};
|
|
76
93
|
/** Every model this file speaks for. */
|
|
77
94
|
type MigrationDecisions = Record<string, ModelDecisions>;
|
|
78
95
|
/** What a decision file default-exports. */
|
|
@@ -89,7 +106,8 @@ interface MigrationFile {
|
|
|
89
106
|
decisions: MigrationDecisions;
|
|
90
107
|
}
|
|
91
108
|
/**
|
|
92
|
-
* Declare what happens to the data under fields this push removes
|
|
109
|
+
* Declare what happens to the data under fields this push removes, and to
|
|
110
|
+
* records that name no owner when a model moves onto an owned plane.
|
|
93
111
|
*
|
|
94
112
|
* Returns a plain object rather than doing anything: a decision file is a
|
|
95
113
|
* declaration that gets committed and reviewed, and code that acted at import
|
|
@@ -97,4 +115,4 @@ interface MigrationFile {
|
|
|
97
115
|
*/
|
|
98
116
|
declare function migration(id: string, decisions: MigrationDecisions): MigrationFile;
|
|
99
117
|
|
|
100
|
-
export { type Drop, type FieldDecision, type FillFrom, type Hold, type JoinFrom, type MigrationDecisions, type MigrationFile, type ModelDecisions, type RemapValues, type RenameTo, type Retype, type SplitInto, migration };
|
|
118
|
+
export { type Drop, type FieldDecision, type FillFrom, type Hold, type JoinFrom, type MigrationDecisions, type MigrationFile, type ModelDecisions, type OwnerDecision, type RemapValues, type RenameTo, type Retype, type SplitInto, migration };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foir/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.1",
|
|
4
4
|
"description": "Universal platform CLI for Foir platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@foir/console-routes": "0.0.0",
|
|
66
66
|
"@foir/proto-ts": "0.109.0",
|
|
67
67
|
"@foir/rpc-node": "0.0.0",
|
|
68
|
-
"@foir/sdk": "0.
|
|
68
|
+
"@foir/sdk": "0.11.0"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">=18.0.0"
|