@eide/foir-cli 0.24.0 → 0.25.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 CHANGED
@@ -1281,6 +1281,7 @@ import {
1281
1281
  FieldSchema as ProtoFieldSchema,
1282
1282
  SharingConfigSchema as ProtoSharingConfigSchema,
1283
1283
  ModelConfigSchema as ProtoModelConfigSchema,
1284
+ LookupDefinitionSchema as ProtoLookupDefinitionSchema,
1284
1285
  CreateModelRequestSchema,
1285
1286
  GetModelRequestSchema,
1286
1287
  GetModelByKeyRequestSchema,
@@ -1328,7 +1329,13 @@ function jsConfigToProto(c) {
1328
1329
  naturalKeyField: c.naturalKeyField,
1329
1330
  systemEntity: c.systemEntity ?? false,
1330
1331
  deletable: c.deletable,
1331
- editable: c.editable
1332
+ editable: c.editable,
1333
+ lookups: c.lookups ? c.lookups.map(
1334
+ (l) => create3(ProtoLookupDefinitionSchema, {
1335
+ keyBy: l.keyBy ?? [],
1336
+ name: l.name
1337
+ })
1338
+ ) : []
1332
1339
  });
1333
1340
  }
1334
1341
  function createModelsMethods(client) {
@@ -1383,7 +1390,8 @@ function createModelsMethods(client) {
1383
1390
  config: params.config ? jsConfigToProto(params.config) : void 0,
1384
1391
  changeDescription: params.changeDescription,
1385
1392
  updatePushSnapshot: params.updatePushSnapshot ?? false,
1386
- snapshotOnly: params.snapshotOnly ?? false
1393
+ snapshotOnly: params.snapshotOnly ?? false,
1394
+ allowLookupRebuild: params.allowLookupRebuild ?? false
1387
1395
  })
1388
1396
  );
1389
1397
  return resp.model ?? null;
@@ -2748,7 +2756,8 @@ function createOperationsMethods(client) {
2748
2756
  precondition: params.precondition,
2749
2757
  configId: params.configId,
2750
2758
  supportsAsync: params.supportsAsync,
2751
- callbackTtlSeconds: params.callbackTtlSeconds
2759
+ callbackTtlSeconds: params.callbackTtlSeconds,
2760
+ capabilities: params.capabilities ?? []
2752
2761
  })
2753
2762
  );
2754
2763
  return resp.operation ?? null;
@@ -2769,7 +2778,8 @@ function createOperationsMethods(client) {
2769
2778
  precondition: params.precondition,
2770
2779
  isActive: params.isActive,
2771
2780
  supportsAsync: params.supportsAsync,
2772
- callbackTtlSeconds: params.callbackTtlSeconds
2781
+ callbackTtlSeconds: params.callbackTtlSeconds,
2782
+ capabilities: params.capabilities
2773
2783
  })
2774
2784
  );
2775
2785
  return resp.operation ?? null;
@@ -5063,7 +5073,15 @@ async function reconcileConfig(client, configId, manifest, options = {}) {
5063
5073
  const operationBaseUrl = manifest.operationBaseUrl ?? "";
5064
5074
  const modelConflicts = [];
5065
5075
  const mappingConflicts = [];
5066
- await reconcileModels(client, configId, manifest.models ?? [], summary, options.force ?? false, modelConflicts);
5076
+ await reconcileModels(
5077
+ client,
5078
+ configId,
5079
+ manifest.models ?? [],
5080
+ summary,
5081
+ options.force ?? false,
5082
+ options.allowLookupRebuild ?? false,
5083
+ modelConflicts
5084
+ );
5067
5085
  await reconcileOperations(client, configId, manifest.operations ?? [], operationBaseUrl, summary);
5068
5086
  await reconcileHooks(client, configId, manifest.hooks ?? [], summary);
5069
5087
  await reconcileSegments(client, configId, manifest.segments ?? [], summary);
@@ -5094,7 +5112,7 @@ async function reconcileConfig(client, configId, manifest, options = {}) {
5094
5112
  }
5095
5113
  return summary;
5096
5114
  }
5097
- async function reconcileModels(client, configId, models, summary, force, conflictOut) {
5115
+ async function reconcileModels(client, configId, models, summary, force, allowLookupRebuild, conflictOut) {
5098
5116
  const existing = await client.models.listModels({ limit: 200 });
5099
5117
  const allByKey = new Map(
5100
5118
  existing.items.map((m) => [m.key, m])
@@ -5111,6 +5129,7 @@ async function reconcileModels(client, configId, models, summary, force, conflic
5111
5129
  if (m.pluralName) config2.pluralName = m.pluralName;
5112
5130
  if (m.pluralKey) config2.pluralKey = m.pluralKey;
5113
5131
  if (m.description) config2.description = m.description;
5132
+ if (m.lookups !== void 0) config2.lookups = m.lookups;
5114
5133
  const ex = allByKey.get(m.key);
5115
5134
  if (!ex) {
5116
5135
  plans.push({ kind: "create", model: m, config: config2 });
@@ -5179,7 +5198,8 @@ async function reconcileModels(client, configId, models, summary, force, conflic
5179
5198
  name: p.name,
5180
5199
  fields: fieldsToWrite,
5181
5200
  config: p.config,
5182
- updatePushSnapshot: true
5201
+ updatePushSnapshot: true,
5202
+ allowLookupRebuild
5183
5203
  });
5184
5204
  summary.models.updated++;
5185
5205
  summary.updatedModelIds.push(p.id);
@@ -5219,6 +5239,7 @@ async function reconcileOperations(client, configId, operations, operationBaseUr
5219
5239
  `\u26A0 operation "${op.key}": mode=async but timeoutMs=${op.timeoutMs} \u2014 ack should return in <10s; long timeouts mask slow extensions`
5220
5240
  );
5221
5241
  }
5242
+ const capabilities = op.capabilities ?? [];
5222
5243
  if (ex) {
5223
5244
  const empty = {};
5224
5245
  await client.operations.updateOperation({
@@ -5235,7 +5256,8 @@ async function reconcileOperations(client, configId, operations, operationBaseUr
5235
5256
  precondition: op.precondition ?? empty,
5236
5257
  isActive: op.isActive,
5237
5258
  supportsAsync,
5238
- callbackTtlSeconds: op.callbackTtlSeconds
5259
+ callbackTtlSeconds: op.callbackTtlSeconds,
5260
+ capabilities
5239
5261
  });
5240
5262
  summary.operations.updated++;
5241
5263
  summary.updatedOperationIds.push(ex.id);
@@ -5257,7 +5279,8 @@ async function reconcileOperations(client, configId, operations, operationBaseUr
5257
5279
  precondition: op.precondition,
5258
5280
  configId,
5259
5281
  supportsAsync,
5260
- callbackTtlSeconds: op.callbackTtlSeconds
5282
+ callbackTtlSeconds: op.callbackTtlSeconds,
5283
+ capabilities
5261
5284
  });
5262
5285
  summary.operations.created++;
5263
5286
  }
@@ -5802,6 +5825,10 @@ function registerPushCommand(program2, globalOpts) {
5802
5825
  "--publish",
5803
5826
  "Promote updated models, operations, auth providers, profile schema, and design tokens to the published channel after the push. New resources auto-publish; this flag covers updates, which are otherwise left as drafts.",
5804
5827
  false
5828
+ ).option(
5829
+ "--rebuild",
5830
+ "Accept lookup renames. A lookup with the same keyBy and a changed `name` override rebuilds its projection rows (old rows are dropped, new rows are re-emitted from records). Without this flag, the server rejects renames with a pointer here. Pure add / remove on lookups does not require this flag.",
5831
+ false
5805
5832
  ).option("--env <path>", "Path to .env file (default: .env)").action(
5806
5833
  withErrorHandler(
5807
5834
  globalOpts,
@@ -5862,7 +5889,8 @@ function registerPushCommand(program2, globalOpts) {
5862
5889
  tenantId: resolved?.project.tenantId,
5863
5890
  projectId: resolved?.project.id,
5864
5891
  force: opts.force ?? false,
5865
- publishDesignTokens: opts.publish ?? false
5892
+ publishDesignTokens: opts.publish ?? false,
5893
+ allowLookupRebuild: opts.rebuild ?? false
5866
5894
  });
5867
5895
  } catch (e) {
5868
5896
  if (e instanceof PushConflictError) {
@@ -67,6 +67,23 @@ type GenericFieldDefinitionInput = BaseFieldDefinitionInput & {
67
67
  };
68
68
  };
69
69
  type FieldDefinitionInput = SelectFieldDefinitionInput | EnumFieldDefinitionInput | GenericFieldDefinitionInput;
70
+ /**
71
+ * Lookup definition — declares one indexed access path on a model.
72
+ *
73
+ * `keyBy` is the ordered list of top-level scalar field keys that make
74
+ * up the lookup key. Single-field lookups are common; composites are
75
+ * first-class (e.g. `['fromHost', 'fromPath']` for a redirect model).
76
+ * `name` overrides the generated GraphQL query field name; omit it to
77
+ * use `<typeLowerCamel>By<PascalCaseKeyFields>`.
78
+ *
79
+ * Validation runs server-side via the platform's config-write RPC —
80
+ * see RFC §Data model → Constraints. The CLI does no local validation
81
+ * here; the server's structured error surfaces verbatim on failure.
82
+ */
83
+ interface LookupDefinitionInput {
84
+ keyBy: string[];
85
+ name?: string;
86
+ }
70
87
  interface ApplyConfigModelInput {
71
88
  key: string;
72
89
  name: string;
@@ -75,6 +92,12 @@ interface ApplyConfigModelInput {
75
92
  description?: string;
76
93
  fields?: FieldDefinitionInput[];
77
94
  config?: Record<string, unknown>;
95
+ /**
96
+ * Lookup definitions for this model. Server validates: existence /
97
+ * scalar-type / top-level-only / caps (4 lookups, 4 fields) / GraphQL
98
+ * field-name regex for `name`. See RFC §Data model (config surface).
99
+ */
100
+ lookups?: LookupDefinitionInput[];
78
101
  }
79
102
  interface QuotaRule {
80
103
  /** Segment key to target (empty string or omitted = default/fallback). */
@@ -168,6 +191,28 @@ interface ApplyConfigOperationInput {
168
191
  callbackTimeoutRetryPolicy?: {
169
192
  maxRetries?: number;
170
193
  };
194
+ /**
195
+ * Capability strings stamped onto the scoped dispatch token (and its
196
+ * callback-window successor) so the extension can call back into
197
+ * api-public with the precise authorities it needs — nothing more.
198
+ *
199
+ * Per-model record access is three-part: `records:read:<model_key>`,
200
+ * `records:write:<model_key>`, `records:delete:<model_key>`,
201
+ * `records:publish:<model_key>`. List every model the operation
202
+ * touches; the unscoped two-part forms (`records:read` etc.) only
203
+ * work for admin API keys and won't satisfy customer-scoped tokens.
204
+ *
205
+ * Cross-cutting caps: `embeddings:read`, `embeddings:write`,
206
+ * `shares:read`, `shares:write`, `files:read`, `files:write`,
207
+ * `config:read`, `status:write`, and the secrets:* family. See
208
+ * `services/internal/scopedtoken/capabilities.go` for the full enum.
209
+ *
210
+ * `operations:complete` is auto-injected on callback tokens — never
211
+ * declare it here. Omitting `capabilities` (or passing an empty array)
212
+ * mints an authentic-but-unprivileged token; useful for operations
213
+ * whose extension makes no back-channel calls.
214
+ */
215
+ capabilities?: string[];
171
216
  }
172
217
  interface ApplyConfigSegmentInput {
173
218
  key: string;
@@ -453,4 +498,4 @@ interface FoirSecretsConfig {
453
498
  */
454
499
  declare function defineSecrets(config: FoirSecretsConfig): FoirSecretsConfig;
455
500
 
456
- export { type AppInput, type AppPlacementFieldChoiceInput, type AppSinkMappingInput, type AppSourceMappingInput, type ApplyConfigApiKeyInput, type ApplyConfigAuthProviderInput, type ApplyConfigDesignTokensInput, type ApplyConfigHookInput, type ApplyConfigInput, type ApplyConfigModelInput, type ApplyConfigOperationInput, type ApplyConfigPlacementInput, type ApplyConfigProjectInput, type ApplyConfigProjectSettingsInput, type ApplyConfigScheduleInput, type ApplyConfigSegmentInput, type EnumFieldConfig, type EnumFieldDefinitionInput, type EnumFieldOption, type ExpressionPrecondition, type FieldDefinitionInput, type FoirSecretsConfig, type Precondition, type QuotaRule, type SecretDeclaration, type SecretOwnerKind, type SegmentPrecondition, type SelectFieldConfig, type SelectFieldDefinitionInput, defineAuthProvider, defineConfig, defineDesignTokens, defineEnumField, defineField, defineHook, defineModel, defineOperation, definePlacement, defineSchedule, defineSecrets, defineSegment, defineSelectField };
501
+ export { type AppInput, type AppPlacementFieldChoiceInput, type AppSinkMappingInput, type AppSourceMappingInput, type ApplyConfigApiKeyInput, type ApplyConfigAuthProviderInput, type ApplyConfigDesignTokensInput, type ApplyConfigHookInput, type ApplyConfigInput, type ApplyConfigModelInput, type ApplyConfigOperationInput, type ApplyConfigPlacementInput, type ApplyConfigProjectInput, type ApplyConfigProjectSettingsInput, type ApplyConfigScheduleInput, type ApplyConfigSegmentInput, type EnumFieldConfig, type EnumFieldDefinitionInput, type EnumFieldOption, type ExpressionPrecondition, type FieldDefinitionInput, type FoirSecretsConfig, type LookupDefinitionInput, type Precondition, type QuotaRule, type SecretDeclaration, type SecretOwnerKind, type SegmentPrecondition, type SelectFieldConfig, type SelectFieldDefinitionInput, defineAuthProvider, defineConfig, defineDesignTokens, defineEnumField, defineField, defineHook, defineModel, defineOperation, definePlacement, defineSchedule, defineSecrets, defineSegment, defineSelectField };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eide/foir-cli",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "Universal platform CLI for Foir platform",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -50,7 +50,7 @@
50
50
  "@bufbuild/protovalidate": "^1.1.1",
51
51
  "@connectrpc/connect": "^2.0.0",
52
52
  "@connectrpc/connect-node": "^2.0.0",
53
- "@eide/foir-proto-ts": "^0.59.0",
53
+ "@eide/foir-proto-ts": "^0.66.0",
54
54
  "chalk": "^5.3.0",
55
55
  "commander": "^12.1.0",
56
56
  "dotenv": "^16.4.5",