@foir/sdk 0.16.0 → 0.18.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/CHANGELOG.md CHANGED
@@ -108,6 +108,57 @@ nobody asking for `@foir/sdk` gets it early. The push that carries the commit to
108
108
 
109
109
  Changes on `dev` that have not been published to npm.
110
110
 
111
+ ## [0.18.0] — 2026-10-02
112
+
113
+ Published under npm's `next` tag from `dev`, because it calls fields the
114
+ platform serves from the next promotion; `latest` moves to it then.
115
+
116
+ ### Changed
117
+
118
+ - **Breaking: an app finds a customer by identity through its install, not
119
+ through a model** (`@foir/sdk/app`). The customer sign-in an app's customers
120
+ come through is paired with the install as a whole, so the two reads that
121
+ walk it no longer name a model and have moved beside the other customer
122
+ reads:
123
+ - `records.customer.identities(model, customerId)` is now
124
+ `customers.identities(customerId)`.
125
+ - `customers.byIdentity(identity)` is new: the id of the customer who holds
126
+ an identity at the app's own system, or `null`. It replaces reading a
127
+ customer's record for its owner.
128
+
129
+ Both need the new capability `customers:read:identity` (or `customers:read`),
130
+ declared by the operation, and are refused at an install that pairs no
131
+ sign-in. Reading or writing a model is no longer what admits them.
132
+ - `install.config()` asks the platform for `customerSignIn`, a field a platform
133
+ older than this release does not have.
134
+
135
+ ### Added
136
+
137
+ - `install.config()` answers `customerSignIn`: the key of the customer sign-in
138
+ the install pairs, or `null` when it pairs none.
139
+
140
+ ## [0.17.0] — 2026-10-02
141
+
142
+ Published under npm's `next` tag from `dev`, because it reads a field the
143
+ platform serves from the next promotion; `latest` moves to it then.
144
+
145
+ ### Added
146
+
147
+ - **An app is told when a project cannot email its customers**
148
+ (`@foir/sdk/app`). A project with no App base URL used to accept a
149
+ notification's email and drop it. The platform now refuses a notification
150
+ that carries an email, and an opt-in to a category confirmed by email, with
151
+ the reason as data. `appCustomerEmailUnavailableOf(error)` reads it:
152
+ `{ reason: 'NO_APP_BASE_URL' }`, or `null` for any other failure.
153
+ - `install.config()` answers `customerEmailUnavailable`: the same reason ahead
154
+ of time, or `null` when the project can send. `AppCustomerEmailUnavailableReason`
155
+ is the type.
156
+
157
+ ### Changed
158
+
159
+ - `install.config()` asks the platform for `customerEmailUnavailable`, a field
160
+ a platform older than this release does not have.
161
+
111
162
  ## [0.16.0] — 2026-10-02
112
163
 
113
164
  Published under npm's `next` tag from `dev`, as 0.15.0 was; `latest` moves to
@@ -28,6 +28,7 @@
28
28
  */
29
29
  import type { FoirDispatchCredential } from '../data/transport';
30
30
  import { type FoirRetryPolicy } from '../transport/retry';
31
+ import type { AppCustomerEmailUnavailableReason } from './errors';
31
32
  type Json = null | boolean | number | string | Json[] | {
32
33
  [key: string]: Json;
33
34
  };
@@ -95,6 +96,21 @@ export interface AppInstallConfig {
95
96
  settings: JsonObject;
96
97
  sources: AppBinding[];
97
98
  sinks: AppBinding[];
99
+ /**
100
+ * Why the project cannot send a customer an email at the level this install
101
+ * sits at, or null when it can. While it is set, a notification that carries
102
+ * an email and an opt-in to a category confirmed by email are refused
103
+ * (`appCustomerEmailUnavailableOf`), so say so before one is.
104
+ */
105
+ customerEmailUnavailable: AppCustomerEmailUnavailableReason | null;
106
+ /**
107
+ * The customer sign-in this install pairs with the app, by key, for an app
108
+ * whose own system has customers; null when it pairs none. Every identity
109
+ * the app names is resolved through it, so while it is null nothing finds a
110
+ * customer by identity: `customers.byIdentity` and `customers.identities`
111
+ * are refused, and an operation that needs them is not registered.
112
+ */
113
+ customerSignIn: string | null;
98
114
  }
99
115
  /**
100
116
  * The status an install reports. Leaving out `subscriptionHealth`,
@@ -370,12 +386,6 @@ export interface AppClient {
370
386
  * else is attached to them. Needs `customers:write` as well.
371
387
  */
372
388
  erase(model: string, identity: string): Promise<AppCustomerEraseResult>;
373
- /**
374
- * The other direction: the identities a customer the app names by id
375
- * signs in with there, most recently seen first. Empty when they sign in
376
- * with none. For an app acting on an event that names the Foir customer.
377
- */
378
- identities(model: string, customerId: string): Promise<string[]>;
379
389
  };
380
390
  };
381
391
  customers: {
@@ -407,6 +417,23 @@ export interface AppClient {
407
417
  * `customers:read`.
408
418
  */
409
419
  byEmail(email: string): Promise<AppCustomer | null>;
420
+ /**
421
+ * The id of the customer who holds this identity at the app's own system,
422
+ * or null when nobody does: for an app hearing from its system about one
423
+ * of its customers. The identity is the subject of the customer's sign-in
424
+ * with that system, and it is resolved through the customer sign-in the
425
+ * install pairs (`install.config().customerSignIn`) and no other. Never
426
+ * matched by email. Needs `customers:read:identity`, or `customers:read`.
427
+ */
428
+ byIdentity(identity: string): Promise<string | null>;
429
+ /**
430
+ * The other direction: the identities a customer the app names by id
431
+ * holds at the app's own system, most recently seen first, through the
432
+ * same pairing. Empty when they have never signed in that way. For an app
433
+ * acting on an event that names the Foir customer. Needs
434
+ * `customers:read:identity`, or `customers:read`.
435
+ */
436
+ identities(customerId: string): Promise<string[]>;
410
437
  /**
411
438
  * The customers at the install's level, a page at a time, newest first.
412
439
  * Needs `customers:read`: an operation declaring only
@@ -117,6 +117,14 @@ export declare type AppConfig = Message<"apps.v1.AppConfig"> & {
117
117
  * @generated from field: repeated apps.v1.Feature features = 13;
118
118
  */
119
119
  features: Feature[];
120
+
121
+ /**
122
+ * OPTIONAL. The app's own system has customers who sign in to it. See
123
+ * CustomerSignIn.
124
+ *
125
+ * @generated from field: apps.v1.CustomerSignIn customer_sign_in = 14;
126
+ */
127
+ customerSignIn?: CustomerSignIn | undefined;
120
128
  };
121
129
 
122
130
  /**
@@ -125,6 +133,42 @@ export declare type AppConfig = Message<"apps.v1.AppConfig"> & {
125
133
  */
126
134
  export declare const AppConfigSchema: GenMessage<AppConfig>;
127
135
 
136
+ /**
137
+ * The app's own system has customers, and the app names each by the subject of
138
+ * their sign-in with that system: a store's customer number, which is also who
139
+ * they are when they sign in to the project through the store's sign-in.
140
+ *
141
+ * Declaring it lets an install pair ONE of the project's customer sign-ins with
142
+ * the app (ConfirmInstallAppRequest.customer_sign_in): the operator says which
143
+ * sign-in the app's customers come through, and the platform resolves every
144
+ * identity the app names through that sign-in and no other. The pairing is the
145
+ * operator's, never inferred: the install knows the external account it
146
+ * connects, not which of the project's sign-ins its customers use, and a wrong
147
+ * pairing would attach one person's data to another.
148
+ *
149
+ * The pairing is optional. What needs it says so, and cannot be on without it:
150
+ * a source that names its customers by identity (SourceType.identity), a
151
+ * feature that finds customers through it (Feature.customer_sign_in), and an
152
+ * operation that asks who holds an identity (customers:read:identity).
153
+ *
154
+ * @generated from message apps.v1.CustomerSignIn
155
+ */
156
+ export declare type CustomerSignIn = Message<"apps.v1.CustomerSignIn"> & {
157
+ /**
158
+ * What the setup tells the operator they are choosing, in a sentence: which
159
+ * customers these are and what identifies them.
160
+ *
161
+ * @generated from field: string description = 1;
162
+ */
163
+ description: string;
164
+ };
165
+
166
+ /**
167
+ * Describes the message apps.v1.CustomerSignIn.
168
+ * Use `create(CustomerSignInSchema)` to create a new message.
169
+ */
170
+ export declare const CustomerSignInSchema: GenMessage<CustomerSignIn>;
171
+
128
172
  /**
129
173
  * A feature of the app: something the operator turns on or off as a whole at
130
174
  * setup, such as a store's preorders or its back-in-stock alerts.
@@ -180,6 +224,16 @@ export declare type Feature = Message<"apps.v1.Feature"> & {
180
224
  * @generated from field: repeated string sources = 5;
181
225
  */
182
226
  sources: string[];
227
+
228
+ /**
229
+ * The feature finds customers through the install's customer sign-in
230
+ * (AppConfig.customer_sign_in), which the manifest must declare. It cannot
231
+ * be on at an install that pairs none: the setup asks for the sign-in at the
232
+ * switch, and the platform refuses the switch without one.
233
+ *
234
+ * @generated from field: bool customer_sign_in = 6;
235
+ */
236
+ customerSignIn: boolean;
183
237
  };
184
238
 
185
239
  /**
@@ -586,10 +640,11 @@ export declare type SourceType = Message<"apps.v1.SourceType"> & {
586
640
  *
587
641
  * It is what lets a sync write customer-owned rows. The platform resolves the
588
642
  * value to a Foir customer through the customer sign-in the install pairs
589
- * with the source (SourceMapping.identity_provider), at the install's level:
590
- * the customer who signs in with that identity, or one made for it. Each
591
- * customer holds one row of the source, their profile, so the source has no
592
- * natural key; the customer is the key. Never matched by email.
643
+ * with the app (AppConfig.customer_sign_in, which the manifest must then
644
+ * declare), at the install's level: the customer who signs in with that
645
+ * identity, or one made for it. Each customer holds one row of the source,
646
+ * their profile, so the source has no natural key; the customer is the key.
647
+ * Never matched by email.
593
648
  *
594
649
  * @generated from field: string identity = 10;
595
650
  */
@@ -1429,6 +1484,18 @@ export enum ManifestCapability {
1429
1484
  */
1430
1485
  CUSTOMERS_READ_NAMED = 15,
1431
1486
 
1487
+ /**
1488
+ * The link between a customer and who they are at the app's own system, and
1489
+ * nothing else about them: the customer who holds an identity, and the
1490
+ * identities a customer the operation names by id holds, both through the
1491
+ * customer sign-in the install pairs with the app and no other. An operation
1492
+ * that declares customers:read holds it too; customers:read:named does not
1493
+ * reach it, nor it what that reaches.
1494
+ *
1495
+ * @generated from enum value: MANIFEST_CAPABILITY_CUSTOMERS_READ_IDENTITY = 16;
1496
+ */
1497
+ CUSTOMERS_READ_IDENTITY = 16,
1498
+
1432
1499
  /**
1433
1500
  * Reporting an ASYNC run's outcome.
1434
1501
  *
@@ -34,6 +34,26 @@ export declare function appCustomerUnlinkedOf(error: unknown): {
34
34
  model: string;
35
35
  identityProvider: string;
36
36
  } | null;
37
+ /**
38
+ * Why a project cannot send a customer an email. `NO_APP_BASE_URL`: no App base
39
+ * URL is set for the project, or for the customer's organization. A reason
40
+ * this build does not know arrives as the platform spelled it.
41
+ */
42
+ export type AppCustomerEmailUnavailableReason = 'NO_APP_BASE_URL' | (string & {});
43
+ /** The reason as the platform sent it, or null when it sent none. */
44
+ export declare function customerEmailUnavailableReasonOf(value: unknown): AppCustomerEmailUnavailableReason | null;
45
+ /**
46
+ * A request refused because it would send a customer an email the project
47
+ * cannot send, or null for any other failure. Nothing was written and nothing
48
+ * was queued. An opt-in to a category confirmed by email is refused this way,
49
+ * and so is a notification that carries an email.
50
+ *
51
+ * It is about the project, not the customer: the project's operator fixes it,
52
+ * and the same request goes once they have.
53
+ */
54
+ export declare function appCustomerEmailUnavailableOf(error: unknown): {
55
+ reason: AppCustomerEmailUnavailableReason;
56
+ } | null;
37
57
  /**
38
58
  * Whether a read failed because the token is not served the field it asked
39
59
  * for: the field is not on the schema this token's capabilities reach. That is
@@ -22,8 +22,8 @@
22
22
  */
23
23
  export { createAppClient } from './client';
24
24
  export type { AppBinding, AppClient, AppClientConfig, AppConsentChange, AppCustomer, AppCustomerEraseResult, AppCustomerList, AppCustomerSaveResult, AppFieldMapping, AppInstallConfig, AppNotification, AppOperationStart, AppRecord, AppRecordList, AppStartedOperation, AppStatusReport, AppSyncDirection, AppUpsertResult, AppWhoami, } from './client';
25
- export { appCustomerUnlinkedOf, appLookupConflictOf, isAppFieldWithheld } from './errors';
26
- export type { AppLookupConflict } from './errors';
25
+ export { appCustomerEmailUnavailableOf, appCustomerUnlinkedOf, appLookupConflictOf, isAppFieldWithheld } from './errors';
26
+ export type { AppCustomerEmailUnavailableReason, AppLookupConflict } from './errors';
27
27
  export { appModelFieldName, appModelTypeName, appSelectorEnumValue } from './naming';
28
28
  export { RECORD_KEY_RULE, recordKeyRefusal } from './record-key';
29
29
  export { readAppDispatch, readCredentialProbe } from './dispatch';
@@ -7,7 +7,7 @@ export declare const APP_HOOK_EVENTS: readonly ["RECORD_CREATED", "RECORD_UPDATE
7
7
  /** An event a hook can name. */
8
8
  export type AppHookEvent = (typeof APP_HOOK_EVENTS)[number];
9
9
  /** The capabilities an operation declares as written. */
10
- export declare const APP_CAPABILITIES: readonly ["config:read", "secrets:read.app", "secrets:put.app", "secrets:list.app", "secrets:delete.app", "status:write", "files:read", "files:list", "files:write", "customers:write", "notifications:write", "notifications:consent", "customers:read", "customers:read:named", "operations:complete"];
10
+ export declare const APP_CAPABILITIES: readonly ["config:read", "secrets:read.app", "secrets:put.app", "secrets:list.app", "secrets:delete.app", "status:write", "files:read", "files:list", "files:write", "customers:write", "notifications:write", "notifications:consent", "customers:read", "customers:read:named", "customers:read:identity", "operations:complete"];
11
11
  /** A capability an operation declares as written. */
12
12
  export type AppCapability = (typeof APP_CAPABILITIES)[number];
13
13
  /**
@@ -8,30 +8,31 @@ var file_buf_validate_validate = /* @__PURE__ */ fileDesc("ChtidWYvdmFsaWRhdGUvd
8
8
 
9
9
  // ../proto-ts/src/apps/v1/apps_pb.js
10
10
  import { file_google_protobuf_descriptor as file_google_protobuf_descriptor2, file_google_protobuf_struct } from "@bufbuild/protobuf/wkt";
11
- var file_apps_v1_apps = /* @__PURE__ */ fileDesc2("ChJhcHBzL3YxL2FwcHMucHJvdG8SB2FwcHMudjEilgUKCUFwcENvbmZpZxI0CgRuYW1lGAEgASgJQia6SCNyIRhAMh1eW2Etel1bYS16MC05XSooLVthLXowLTldKykqJBIYCgd2ZXJzaW9uGAIgASgJQge6SARyAhABEiMKCG1ldGFkYXRhGAMgASgLMhEuYXBwcy52MS5NZXRhZGF0YRInCgptaWRkbGV3YXJlGAQgASgLMhMuYXBwcy52MS5NaWRkbGV3YXJlEikKC2NyZWRlbnRpYWxzGAUgASgLMhQuYXBwcy52MS5DcmVkZW50aWFscxIwCg9zZXR0aW5nc19zY2hlbWEYBiABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0EjAKB3NvdXJjZXMYByADKAsyHy5hcHBzLnYxLkFwcENvbmZpZy5Tb3VyY2VzRW50cnkSLAoFc2lua3MYCCADKAsyHS5hcHBzLnYxLkFwcENvbmZpZy5TaW5rc0VudHJ5EiYKCm9wZXJhdGlvbnMYCSADKAsyEi5hcHBzLnYxLk9wZXJhdGlvbhIcCgVob29rcxgKIAMoCzINLmFwcHMudjEuSG9vaxIXCgJ1aRgLIAEoCzILLmFwcHMudjEuVUkSIQoHaW5ib3VuZBgMIAEoCzIQLmFwcHMudjEuSW5ib3VuZBIiCghmZWF0dXJlcxgNIAMoCzIQLmFwcHMudjEuRmVhdHVyZRpDCgxTb3VyY2VzRW50cnkSCwoDa2V5GAEgASgJEiIKBXZhbHVlGAIgASgLMhMuYXBwcy52MS5Tb3VyY2VUeXBlOgI4ARpDCgpTaW5rc0VudHJ5EgsKA2tleRgBIAEoCRIkCgV2YWx1ZRgCIAEoCzIVLmFwcHMudjEuU2lua0NvbnRyYWN0OgI4ASKNAQoHRmVhdHVyZRIzCgNrZXkYASABKAlCJrpII3IhGEAyHV5bYS16XVthLXowLTldKigtW2EtejAtOV0rKSokEhYKBXRpdGxlGAIgASgJQge6SARyAhABEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg8KB3NldHRpbmcYBCABKAkSDwoHc291cmNlcxgFIAMoCSJlCghNZXRhZGF0YRIUCgxkaXNwbGF5X25hbWUYASABKAkSEwoLZGVzY3JpcHRpb24YAiABKAkSDAoEaWNvbhgDIAEoCRIOCgZhdXRob3IYBCABKAkSEAoIaG9tZXBhZ2UYBSABKAkiLQoKTWlkZGxld2FyZRIfCgN1cmwYASABKAlCErpID3INMgteaHR0cHM6Ly8uKyLXAgoLQ3JlZGVudGlhbHMSLwoIc3RyYXRlZ3kYASABKA4yHS5hcHBzLnYxLkNyZWRlbnRpYWxzLlN0cmF0ZWd5EiwKBnNjaGVtYRgCIAEoCzIXLmdvb2dsZS5wcm90b2J1Zi5TdHJ1Y3RIAIgBARIoCgVvYXV0aBgDIAEoCzIULmFwcHMudjEuT0F1dGhDb25maWdIAYgBASKpAQoIU3RyYXRlZ3kSGAoUU1RSQVRFR1lfVU5TUEVDSUZJRUQQABISCg5TVFJBVEVHWV9PQVVUSBABEhQKEFNUUkFURUdZX0FQSV9LRVkQAhIaChZTVFJBVEVHWV9TSEFSRURfU0VDUkVUEAMSFAoQU1RSQVRFR1lfU1NIX0tFWRAEEhEKDVNUUkFURUdZX05PTkUQBRIUChBTVFJBVEVHWV9NQU5BR0VEEAZCCQoHX3NjaGVtYUIICgZfb2F1dGgiTAoLT0F1dGhDb25maWcSHAoUYXV0aG9yaXplX3N0YXJ0X3BhdGgYASABKAkSHwoXYXV0aG9yaXplX2NhbGxiYWNrX3BhdGgYAiABKAkiVgoHSW5ib3VuZBIhCgd3ZWJob29rGAEgASgLMhAuYXBwcy52MS5XZWJob29rEigKB2FjY291bnQYAiABKAsyFy5hcHBzLnYxLkFjY291bnRXZWJob29rIhcKB1dlYmhvb2sSDAoEcGF0aBgBIAEoCSI/Cg5BY2NvdW50V2ViaG9vaxIMCgRwYXRoGAEgASgJEg8KB3NldHRpbmcYAiABKAkSDgoGc291cmNlGAMgASgJIt0CCgpTb3VyY2VUeXBlEg0KBWxhYmVsGAEgASgJEi8KBmZpZWxkcxgCIAMoCzIfLmFwcHMudjEuU291cmNlVHlwZS5GaWVsZHNFbnRyeRITCgtuYXR1cmFsX2tleRgDIAEoCRIQCghvcHRpb25hbBgEIAEoCBIWCg5wcm92aXNpb25fb25seRgFIAEoCBIrCglyZWZlcmVuY2UYBiABKAsyGC5hcHBzLnYxLlNvdXJjZVJlZmVyZW5jZRIkCgVvd25lchgHIAEoDjIVLmFwcHMudjEuU3RvcmFnZU93bmVyEhMKC3JlY29tbWVuZGVkGAggASgIEg4KBmxvb2t1cBgJIAMoCRIQCghpZGVudGl0eRgKIAEoCRpGCgtGaWVsZHNFbnRyeRILCgNrZXkYASABKAkSJgoFdmFsdWUYAiABKAsyFy5hcHBzLnYxLlN1Z2dlc3RlZEZpZWxkOgI4ASKqAgoPU291cmNlUmVmZXJlbmNlEhEKCWtleV9maWVsZBgBIAEoCRI0CgZmaWVsZHMYAiADKAsyJC5hcHBzLnYxLlNvdXJjZVJlZmVyZW5jZS5GaWVsZHNFbnRyeRIQCghlbmRwb2ludBgDIAEoCRINCgVob3N0cxgEIAMoCRIqCgdyZXF1ZXN0GAUgASgLMhkuYXBwcy52MS5SZWZlcmVuY2VSZXF1ZXN0EhoKEnJlc3BvbnNlX3RyYW5zZm9ybRgGIAEoCRIdChVjYWNoZV9tYXhfYWdlX3NlY29uZHMYByABKAUaRgoLRmllbGRzRW50cnkSCwoDa2V5GAEgASgJEiYKBXZhbHVlGAIgASgLMhcuYXBwcy52MS5TdWdnZXN0ZWRGaWVsZDoCOAEiswIKEFJlZmVyZW5jZVJlcXVlc3QSDgoGbWV0aG9kGAEgASgJEhUKDXBhdGhfdGVtcGxhdGUYAiABKAkSMwoFcXVlcnkYAyADKAsyJC5hcHBzLnYxLlJlZmVyZW5jZVJlcXVlc3QuUXVlcnlFbnRyeRI3CgdoZWFkZXJzGAQgAygLMiYuYXBwcy52MS5SZWZlcmVuY2VSZXF1ZXN0LkhlYWRlcnNFbnRyeRIVCg1ib2R5X3RlbXBsYXRlGAUgASgJEhUKDWJhdGNoX2NhcGFibGUYBiABKAgaLAoKUXVlcnlFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBGi4KDEhlYWRlcnNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBIroBCgxTaW5rQ29udHJhY3QSDQoFbGFiZWwYASABKAkSMQoGZmllbGRzGAIgAygLMiEuYXBwcy52MS5TaW5rQ29udHJhY3QuRmllbGRzRW50cnkSEwoLbmF0dXJhbF9rZXkYAyABKAkSEAoIb3B0aW9uYWwYBCABKAgaQQoLRmllbGRzRW50cnkSCwoDa2V5GAEgASgJEiEKBXZhbHVlGAIgASgLMhIuYXBwcy52MS5TaW5rRmllbGQ6AjgBIpYCCg5TdWdnZXN0ZWRGaWVsZBIMCgR0eXBlGAEgASgJEhIKBWxhYmVsGAIgASgJSACIAQESFQoIcmVxdWlyZWQYAyABKAhIAYgBARIsCgZjb25maWcYBCABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0SAKIAQESPQoTc3VwcG9ydGVkX2RpcmVjdGlvbhgFIAEoDjIbLmFwcHMudjEuRmllbGRTeW5jRGlyZWN0aW9uSAOIAQESFgoJcXVlcnlhYmxlGAYgASgISASIAQFCCAoGX2xhYmVsQgsKCV9yZXF1aXJlZEIJCgdfY29uZmlnQhYKFF9zdXBwb3J0ZWRfZGlyZWN0aW9uQgwKCl9xdWVyeWFibGUiWwoJU2lua0ZpZWxkEgwKBHR5cGUYASABKAkSEAoIc2VtYW50aWMYAiABKAkSEAoIcmVxdWlyZWQYAyABKAgSEgoFbGFiZWwYBCABKAlIAIgBAUIICgZfbGFiZWwimAQKCU9wZXJhdGlvbhI6CgNrZXkYASABKAlCLbpIKnIoGEAyJF4oX18pP1thLXpdW2EtejAtOV9dKigtW2EtejAtOV9dKykqJBIMCgRuYW1lGAIgASgJEhgKC2Rlc2NyaXB0aW9uGAMgASgJSACIAQESHQoIZW5kcG9pbnQYBCABKAlCC7pICHIGMgReLy4qEhQKDGNhcGFiaWxpdGllcxgFIAMoCRIXCgp0aW1lb3V0X21zGAYgASgFSAGIAQESMgoMcmV0cnlfcG9saWN5GAcgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdEgCiAEBEjIKDGlucHV0X3NjaGVtYRgIIAEoCzIXLmdvb2dsZS5wcm90b2J1Zi5TdHJ1Y3RIA4gBARIzCg1vdXRwdXRfc2NoZW1hGAkgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdEgEiAEBEhsKDnN1cHBvcnRzX2FzeW5jGAogASgISAWIAQESGQoRcnVuX2FmdGVyX2luc3RhbGwYCyABKAgSHgoWcnVuc19vbl9tYXBwZWRfc291cmNlcxgMIAEoCEIOCgxfZGVzY3JpcHRpb25CDQoLX3RpbWVvdXRfbXNCDwoNX3JldHJ5X3BvbGljeUIPCg1faW5wdXRfc2NoZW1hQhAKDl9vdXRwdXRfc2NoZW1hQhEKD19zdXBwb3J0c19hc3luYyI9ChZNYW5pZmVzdENhcGFiaWxpdHlTcGVjEhAKCGRlY2xhcmVzGAEgASgJEhEKCXBlcl9tb2RlbBgCIAEoCCKmAQoESG9vaxIzCgNrZXkYASABKAlCJrpII3IhGEAyHV5bYS16XVthLXowLTldKigtW2EtejAtOV0rKSokEgwKBG5hbWUYAiABKAkSFgoFZXZlbnQYAyABKAlCB7pIBHICEAESJwoGZmlsdGVyGAQgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdBIaCglvcGVyYXRpb24YBSABKAlCB7pIBHICEAEiNwoCVUkSMQoKcGxhY2VtZW50cxgBIAMoCzIdLmFwcHMudjEuUGxhY2VtZW50RGVjbGFyYXRpb24itgQKFFBsYWNlbWVudERlY2xhcmF0aW9uEjMKA2tleRgBIAEoCUImukgjciEYQDIdXlthLXpdW2EtejAtOV0qKC1bYS16MC05XSspKiQSNAoGdGFyZ2V0GAIgASgOMiQuYXBwcy52MS5QbGFjZW1lbnREZWNsYXJhdGlvbi5UYXJnZXQSEgoFbW9kZWwYAyABKAlIAIgBARISCgVmaWVsZBgEIAEoCUgBiAEBEiEKGWZpZWxkX3NlbGVjdGVkX2F0X2luc3RhbGwYBSABKAgSEAoDdGFiGAYgASgJSAKIAQESFgoFdGl0bGUYByABKAlCB7pIBHICEAESHwoDdXJsGAggASgJQhK6SA9yDTILXmh0dHBzOi8vLisSHQoQcmVwbGFjZXNfZGVmYXVsdBgJIAEoCEgDiAEBEisKBWhpbnRzGAogASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdEgEiAEBEg4KBnNjb3BlcxgLIAMoCRIQCghzZXR0aW5ncxgMIAMoCSJ0CgZUYXJnZXQSFgoSVEFSR0VUX1VOU1BFQ0lGSUVEEAASFAoQVEFSR0VUX0ZVTExfUEFHRRABEhYKElRBUkdFVF9NQUlOX0VESVRPUhACEhIKDlRBUkdFVF9TSURFQkFSEAMSEAoMVEFSR0VUX0ZJRUxEEARCCAoGX21vZGVsQggKBl9maWVsZEIGCgRfdGFiQhMKEV9yZXBsYWNlc19kZWZhdWx0QggKBl9oaW50cyqsAQoSRmllbGRTeW5jRGlyZWN0aW9uEiQKIEZJRUxEX1NZTkNfRElSRUNUSU9OX1VOU1BFQ0lGSUVEEAASJAogRklFTERfU1lOQ19ESVJFQ1RJT05fU09VUkNFX09OTFkQARIiCh5GSUVMRF9TWU5DX0RJUkVDVElPTl9TSU5LX09OTFkQAhImCiJGSUVMRF9TWU5DX0RJUkVDVElPTl9CSURJUkVDVElPTkFMEAMqhAEKDFN0b3JhZ2VPd25lchIdChlTVE9SQUdFX09XTkVSX1VOU1BFQ0lGSUVEEAASGQoVU1RPUkFHRV9PV05FUl9QUk9KRUNUEAESHgoaU1RPUkFHRV9PV05FUl9PUkdBTklaQVRJT04QAhIaChZTVE9SQUdFX09XTkVSX0NVU1RPTUVSEAMqxAoKEk1hbmlmZXN0Q2FwYWJpbGl0eRIjCh9NQU5JRkVTVF9DQVBBQklMSVRZX1VOU1BFQ0lGSUVEEAASNgofTUFOSUZFU1RfQ0FQQUJJTElUWV9DT05GSUdfUkVBRBABGhGaph0NCgtjb25maWc6cmVhZBJACiRNQU5JRkVTVF9DQVBBQklMSVRZX1NFQ1JFVFNfUkVBRF9BUFAQAhoWmqYdEgoQc2VjcmV0czpyZWFkLmFwcBI+CiNNQU5JRkVTVF9DQVBBQklMSVRZX1NFQ1JFVFNfUFVUX0FQUBADGhWaph0RCg9zZWNyZXRzOnB1dC5hcHASQAokTUFOSUZFU1RfQ0FQQUJJTElUWV9TRUNSRVRTX0xJU1RfQVBQEAQaFpqmHRIKEHNlY3JldHM6bGlzdC5hcHASRAomTUFOSUZFU1RfQ0FQQUJJTElUWV9TRUNSRVRTX0RFTEVURV9BUFAQBRoYmqYdFAoSc2VjcmV0czpkZWxldGUuYXBwEjgKIE1BTklGRVNUX0NBUEFCSUxJVFlfU1RBVFVTX1dSSVRFEAYaEpqmHQ4KDHN0YXR1czp3cml0ZRI0Ch5NQU5JRkVTVF9DQVBBQklMSVRZX0ZJTEVTX1JFQUQQBxoQmqYdDAoKZmlsZXM6cmVhZBI0Ch5NQU5JRkVTVF9DQVBBQklMSVRZX0ZJTEVTX0xJU1QQCBoQmqYdDAoKZmlsZXM6bGlzdBI2Ch9NQU5JRkVTVF9DQVBBQklMSVRZX0ZJTEVTX1dSSVRFEAkaEZqmHQ0KC2ZpbGVzOndyaXRlEj4KI01BTklGRVNUX0NBUEFCSUxJVFlfQ1VTVE9NRVJTX1dSSVRFEAoaFZqmHREKD2N1c3RvbWVyczp3cml0ZRJGCidNQU5JRkVTVF9DQVBBQklMSVRZX05PVElGSUNBVElPTlNfV1JJVEUQCxoZmqYdFQoTbm90aWZpY2F0aW9uczp3cml0ZRJKCilNQU5JRkVTVF9DQVBBQklMSVRZX05PVElGSUNBVElPTlNfQ09OU0VOVBAMGhuaph0XChVub3RpZmljYXRpb25zOmNvbnNlbnQSPAoiTUFOSUZFU1RfQ0FQQUJJTElUWV9DVVNUT01FUlNfUkVBRBAOGhSaph0QCg5jdXN0b21lcnM6cmVhZBJICihNQU5JRkVTVF9DQVBBQklMSVRZX0NVU1RPTUVSU19SRUFEX05BTUVEEA8aGpqmHRYKFGN1c3RvbWVyczpyZWFkOm5hbWVkEkYKJ01BTklGRVNUX0NBUEFCSUxJVFlfT1BFUkFUSU9OU19DT01QTEVURRANGhmaph0VChNvcGVyYXRpb25zOmNvbXBsZXRlEjsKIE1BTklGRVNUX0NBUEFCSUxJVFlfUkVDT1JEU19SRUFEEBQaFZqmHREKDXJlY29yZHM6cmVhZDoQARI9CiFNQU5JRkVTVF9DQVBBQklMSVRZX1JFQ09SRFNfV1JJVEUQFRoWmqYdEgoOcmVjb3Jkczp3cml0ZToQARI/CiJNQU5JRkVTVF9DQVBBQklMSVRZX1JFQ09SRFNfREVMRVRFEBYaF5qmHRMKD3JlY29yZHM6ZGVsZXRlOhABEkEKI01BTklGRVNUX0NBUEFCSUxJVFlfUkVDT1JEU19QVUJMSVNIEBcaGJqmHRQKEHJlY29yZHM6cHVibGlzaDoQARJFCiVNQU5JRkVTVF9DQVBBQklMSVRZX1JFQ09SRFNfQVRUUklCVVRFEBgaGpqmHRYKEnJlY29yZHM6YXR0cmlidXRlOhABOnUKE21hbmlmZXN0X2NhcGFiaWxpdHkSIS5nb29nbGUucHJvdG9idWYuRW51bVZhbHVlT3B0aW9ucxjj1AMgASgLMh8uYXBwcy52MS5NYW5pZmVzdENhcGFiaWxpdHlTcGVjUhJtYW5pZmVzdENhcGFiaWxpdHlCNVozZ2l0aHViLmNvbS9laWRlc3R1ZGlvL2ZvaXIvZ2VuL3Byb3RvL2FwcHMvdjE7YXBwc3YxYgZwcm90bzM", [file_buf_validate_validate, file_google_protobuf_descriptor2, file_google_protobuf_struct]);
11
+ var file_apps_v1_apps = /* @__PURE__ */ fileDesc2("ChJhcHBzL3YxL2FwcHMucHJvdG8SB2FwcHMudjEiyQUKCUFwcENvbmZpZxI0CgRuYW1lGAEgASgJQia6SCNyIRhAMh1eW2Etel1bYS16MC05XSooLVthLXowLTldKykqJBIYCgd2ZXJzaW9uGAIgASgJQge6SARyAhABEiMKCG1ldGFkYXRhGAMgASgLMhEuYXBwcy52MS5NZXRhZGF0YRInCgptaWRkbGV3YXJlGAQgASgLMhMuYXBwcy52MS5NaWRkbGV3YXJlEikKC2NyZWRlbnRpYWxzGAUgASgLMhQuYXBwcy52MS5DcmVkZW50aWFscxIwCg9zZXR0aW5nc19zY2hlbWEYBiABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0EjAKB3NvdXJjZXMYByADKAsyHy5hcHBzLnYxLkFwcENvbmZpZy5Tb3VyY2VzRW50cnkSLAoFc2lua3MYCCADKAsyHS5hcHBzLnYxLkFwcENvbmZpZy5TaW5rc0VudHJ5EiYKCm9wZXJhdGlvbnMYCSADKAsyEi5hcHBzLnYxLk9wZXJhdGlvbhIcCgVob29rcxgKIAMoCzINLmFwcHMudjEuSG9vaxIXCgJ1aRgLIAEoCzILLmFwcHMudjEuVUkSIQoHaW5ib3VuZBgMIAEoCzIQLmFwcHMudjEuSW5ib3VuZBIiCghmZWF0dXJlcxgNIAMoCzIQLmFwcHMudjEuRmVhdHVyZRIxChBjdXN0b21lcl9zaWduX2luGA4gASgLMhcuYXBwcy52MS5DdXN0b21lclNpZ25JbhpDCgxTb3VyY2VzRW50cnkSCwoDa2V5GAEgASgJEiIKBXZhbHVlGAIgASgLMhMuYXBwcy52MS5Tb3VyY2VUeXBlOgI4ARpDCgpTaW5rc0VudHJ5EgsKA2tleRgBIAEoCRIkCgV2YWx1ZRgCIAEoCzIVLmFwcHMudjEuU2lua0NvbnRyYWN0OgI4ASIlCg5DdXN0b21lclNpZ25JbhITCgtkZXNjcmlwdGlvbhgBIAEoCSKnAQoHRmVhdHVyZRIzCgNrZXkYASABKAlCJrpII3IhGEAyHV5bYS16XVthLXowLTldKigtW2EtejAtOV0rKSokEhYKBXRpdGxlGAIgASgJQge6SARyAhABEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg8KB3NldHRpbmcYBCABKAkSDwoHc291cmNlcxgFIAMoCRIYChBjdXN0b21lcl9zaWduX2luGAYgASgIImUKCE1ldGFkYXRhEhQKDGRpc3BsYXlfbmFtZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCRIMCgRpY29uGAMgASgJEg4KBmF1dGhvchgEIAEoCRIQCghob21lcGFnZRgFIAEoCSItCgpNaWRkbGV3YXJlEh8KA3VybBgBIAEoCUISukgPcg0yC15odHRwczovLy4rItcCCgtDcmVkZW50aWFscxIvCghzdHJhdGVneRgBIAEoDjIdLmFwcHMudjEuQ3JlZGVudGlhbHMuU3RyYXRlZ3kSLAoGc2NoZW1hGAIgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdEgAiAEBEigKBW9hdXRoGAMgASgLMhQuYXBwcy52MS5PQXV0aENvbmZpZ0gBiAEBIqkBCghTdHJhdGVneRIYChRTVFJBVEVHWV9VTlNQRUNJRklFRBAAEhIKDlNUUkFURUdZX09BVVRIEAESFAoQU1RSQVRFR1lfQVBJX0tFWRACEhoKFlNUUkFURUdZX1NIQVJFRF9TRUNSRVQQAxIUChBTVFJBVEVHWV9TU0hfS0VZEAQSEQoNU1RSQVRFR1lfTk9ORRAFEhQKEFNUUkFURUdZX01BTkFHRUQQBkIJCgdfc2NoZW1hQggKBl9vYXV0aCJMCgtPQXV0aENvbmZpZxIcChRhdXRob3JpemVfc3RhcnRfcGF0aBgBIAEoCRIfChdhdXRob3JpemVfY2FsbGJhY2tfcGF0aBgCIAEoCSJWCgdJbmJvdW5kEiEKB3dlYmhvb2sYASABKAsyEC5hcHBzLnYxLldlYmhvb2sSKAoHYWNjb3VudBgCIAEoCzIXLmFwcHMudjEuQWNjb3VudFdlYmhvb2siFwoHV2ViaG9vaxIMCgRwYXRoGAEgASgJIj8KDkFjY291bnRXZWJob29rEgwKBHBhdGgYASABKAkSDwoHc2V0dGluZxgCIAEoCRIOCgZzb3VyY2UYAyABKAki3QIKClNvdXJjZVR5cGUSDQoFbGFiZWwYASABKAkSLwoGZmllbGRzGAIgAygLMh8uYXBwcy52MS5Tb3VyY2VUeXBlLkZpZWxkc0VudHJ5EhMKC25hdHVyYWxfa2V5GAMgASgJEhAKCG9wdGlvbmFsGAQgASgIEhYKDnByb3Zpc2lvbl9vbmx5GAUgASgIEisKCXJlZmVyZW5jZRgGIAEoCzIYLmFwcHMudjEuU291cmNlUmVmZXJlbmNlEiQKBW93bmVyGAcgASgOMhUuYXBwcy52MS5TdG9yYWdlT3duZXISEwoLcmVjb21tZW5kZWQYCCABKAgSDgoGbG9va3VwGAkgAygJEhAKCGlkZW50aXR5GAogASgJGkYKC0ZpZWxkc0VudHJ5EgsKA2tleRgBIAEoCRImCgV2YWx1ZRgCIAEoCzIXLmFwcHMudjEuU3VnZ2VzdGVkRmllbGQ6AjgBIqoCCg9Tb3VyY2VSZWZlcmVuY2USEQoJa2V5X2ZpZWxkGAEgASgJEjQKBmZpZWxkcxgCIAMoCzIkLmFwcHMudjEuU291cmNlUmVmZXJlbmNlLkZpZWxkc0VudHJ5EhAKCGVuZHBvaW50GAMgASgJEg0KBWhvc3RzGAQgAygJEioKB3JlcXVlc3QYBSABKAsyGS5hcHBzLnYxLlJlZmVyZW5jZVJlcXVlc3QSGgoScmVzcG9uc2VfdHJhbnNmb3JtGAYgASgJEh0KFWNhY2hlX21heF9hZ2Vfc2Vjb25kcxgHIAEoBRpGCgtGaWVsZHNFbnRyeRILCgNrZXkYASABKAkSJgoFdmFsdWUYAiABKAsyFy5hcHBzLnYxLlN1Z2dlc3RlZEZpZWxkOgI4ASKzAgoQUmVmZXJlbmNlUmVxdWVzdBIOCgZtZXRob2QYASABKAkSFQoNcGF0aF90ZW1wbGF0ZRgCIAEoCRIzCgVxdWVyeRgDIAMoCzIkLmFwcHMudjEuUmVmZXJlbmNlUmVxdWVzdC5RdWVyeUVudHJ5EjcKB2hlYWRlcnMYBCADKAsyJi5hcHBzLnYxLlJlZmVyZW5jZVJlcXVlc3QuSGVhZGVyc0VudHJ5EhUKDWJvZHlfdGVtcGxhdGUYBSABKAkSFQoNYmF0Y2hfY2FwYWJsZRgGIAEoCBosCgpRdWVyeUVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEaLgoMSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiugEKDFNpbmtDb250cmFjdBINCgVsYWJlbBgBIAEoCRIxCgZmaWVsZHMYAiADKAsyIS5hcHBzLnYxLlNpbmtDb250cmFjdC5GaWVsZHNFbnRyeRITCgtuYXR1cmFsX2tleRgDIAEoCRIQCghvcHRpb25hbBgEIAEoCBpBCgtGaWVsZHNFbnRyeRILCgNrZXkYASABKAkSIQoFdmFsdWUYAiABKAsyEi5hcHBzLnYxLlNpbmtGaWVsZDoCOAEilgIKDlN1Z2dlc3RlZEZpZWxkEgwKBHR5cGUYASABKAkSEgoFbGFiZWwYAiABKAlIAIgBARIVCghyZXF1aXJlZBgDIAEoCEgBiAEBEiwKBmNvbmZpZxgEIAEoCzIXLmdvb2dsZS5wcm90b2J1Zi5TdHJ1Y3RIAogBARI9ChNzdXBwb3J0ZWRfZGlyZWN0aW9uGAUgASgOMhsuYXBwcy52MS5GaWVsZFN5bmNEaXJlY3Rpb25IA4gBARIWCglxdWVyeWFibGUYBiABKAhIBIgBAUIICgZfbGFiZWxCCwoJX3JlcXVpcmVkQgkKB19jb25maWdCFgoUX3N1cHBvcnRlZF9kaXJlY3Rpb25CDAoKX3F1ZXJ5YWJsZSJbCglTaW5rRmllbGQSDAoEdHlwZRgBIAEoCRIQCghzZW1hbnRpYxgCIAEoCRIQCghyZXF1aXJlZBgDIAEoCBISCgVsYWJlbBgEIAEoCUgAiAEBQggKBl9sYWJlbCKYBAoJT3BlcmF0aW9uEjoKA2tleRgBIAEoCUItukgqcigYQDIkXihfXyk/W2Etel1bYS16MC05X10qKC1bYS16MC05X10rKSokEgwKBG5hbWUYAiABKAkSGAoLZGVzY3JpcHRpb24YAyABKAlIAIgBARIdCghlbmRwb2ludBgEIAEoCUILukgIcgYyBF4vLioSFAoMY2FwYWJpbGl0aWVzGAUgAygJEhcKCnRpbWVvdXRfbXMYBiABKAVIAYgBARIyCgxyZXRyeV9wb2xpY3kYByABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0SAKIAQESMgoMaW5wdXRfc2NoZW1hGAggASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdEgDiAEBEjMKDW91dHB1dF9zY2hlbWEYCSABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0SASIAQESGwoOc3VwcG9ydHNfYXN5bmMYCiABKAhIBYgBARIZChFydW5fYWZ0ZXJfaW5zdGFsbBgLIAEoCBIeChZydW5zX29uX21hcHBlZF9zb3VyY2VzGAwgASgIQg4KDF9kZXNjcmlwdGlvbkINCgtfdGltZW91dF9tc0IPCg1fcmV0cnlfcG9saWN5Qg8KDV9pbnB1dF9zY2hlbWFCEAoOX291dHB1dF9zY2hlbWFCEQoPX3N1cHBvcnRzX2FzeW5jIj0KFk1hbmlmZXN0Q2FwYWJpbGl0eVNwZWMSEAoIZGVjbGFyZXMYASABKAkSEQoJcGVyX21vZGVsGAIgASgIIqYBCgRIb29rEjMKA2tleRgBIAEoCUImukgjciEYQDIdXlthLXpdW2EtejAtOV0qKC1bYS16MC05XSspKiQSDAoEbmFtZRgCIAEoCRIWCgVldmVudBgDIAEoCUIHukgEcgIQARInCgZmaWx0ZXIYBCABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0EhoKCW9wZXJhdGlvbhgFIAEoCUIHukgEcgIQASI3CgJVSRIxCgpwbGFjZW1lbnRzGAEgAygLMh0uYXBwcy52MS5QbGFjZW1lbnREZWNsYXJhdGlvbiK2BAoUUGxhY2VtZW50RGVjbGFyYXRpb24SMwoDa2V5GAEgASgJQia6SCNyIRhAMh1eW2Etel1bYS16MC05XSooLVthLXowLTldKykqJBI0CgZ0YXJnZXQYAiABKA4yJC5hcHBzLnYxLlBsYWNlbWVudERlY2xhcmF0aW9uLlRhcmdldBISCgVtb2RlbBgDIAEoCUgAiAEBEhIKBWZpZWxkGAQgASgJSAGIAQESIQoZZmllbGRfc2VsZWN0ZWRfYXRfaW5zdGFsbBgFIAEoCBIQCgN0YWIYBiABKAlIAogBARIWCgV0aXRsZRgHIAEoCUIHukgEcgIQARIfCgN1cmwYCCABKAlCErpID3INMgteaHR0cHM6Ly8uKxIdChByZXBsYWNlc19kZWZhdWx0GAkgASgISAOIAQESKwoFaGludHMYCiABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0SASIAQESDgoGc2NvcGVzGAsgAygJEhAKCHNldHRpbmdzGAwgAygJInQKBlRhcmdldBIWChJUQVJHRVRfVU5TUEVDSUZJRUQQABIUChBUQVJHRVRfRlVMTF9QQUdFEAESFgoSVEFSR0VUX01BSU5fRURJVE9SEAISEgoOVEFSR0VUX1NJREVCQVIQAxIQCgxUQVJHRVRfRklFTEQQBEIICgZfbW9kZWxCCAoGX2ZpZWxkQgYKBF90YWJCEwoRX3JlcGxhY2VzX2RlZmF1bHRCCAoGX2hpbnRzKqwBChJGaWVsZFN5bmNEaXJlY3Rpb24SJAogRklFTERfU1lOQ19ESVJFQ1RJT05fVU5TUEVDSUZJRUQQABIkCiBGSUVMRF9TWU5DX0RJUkVDVElPTl9TT1VSQ0VfT05MWRABEiIKHkZJRUxEX1NZTkNfRElSRUNUSU9OX1NJTktfT05MWRACEiYKIkZJRUxEX1NZTkNfRElSRUNUSU9OX0JJRElSRUNUSU9OQUwQAyqEAQoMU3RvcmFnZU93bmVyEh0KGVNUT1JBR0VfT1dORVJfVU5TUEVDSUZJRUQQABIZChVTVE9SQUdFX09XTkVSX1BST0pFQ1QQARIeChpTVE9SQUdFX09XTkVSX09SR0FOSVpBVElPThACEhoKFlNUT1JBR0VfT1dORVJfQ1VTVE9NRVIQAyqUCwoSTWFuaWZlc3RDYXBhYmlsaXR5EiMKH01BTklGRVNUX0NBUEFCSUxJVFlfVU5TUEVDSUZJRUQQABI2Ch9NQU5JRkVTVF9DQVBBQklMSVRZX0NPTkZJR19SRUFEEAEaEZqmHQ0KC2NvbmZpZzpyZWFkEkAKJE1BTklGRVNUX0NBUEFCSUxJVFlfU0VDUkVUU19SRUFEX0FQUBACGhaaph0SChBzZWNyZXRzOnJlYWQuYXBwEj4KI01BTklGRVNUX0NBUEFCSUxJVFlfU0VDUkVUU19QVVRfQVBQEAMaFZqmHREKD3NlY3JldHM6cHV0LmFwcBJACiRNQU5JRkVTVF9DQVBBQklMSVRZX1NFQ1JFVFNfTElTVF9BUFAQBBoWmqYdEgoQc2VjcmV0czpsaXN0LmFwcBJECiZNQU5JRkVTVF9DQVBBQklMSVRZX1NFQ1JFVFNfREVMRVRFX0FQUBAFGhiaph0UChJzZWNyZXRzOmRlbGV0ZS5hcHASOAogTUFOSUZFU1RfQ0FQQUJJTElUWV9TVEFUVVNfV1JJVEUQBhoSmqYdDgoMc3RhdHVzOndyaXRlEjQKHk1BTklGRVNUX0NBUEFCSUxJVFlfRklMRVNfUkVBRBAHGhCaph0MCgpmaWxlczpyZWFkEjQKHk1BTklGRVNUX0NBUEFCSUxJVFlfRklMRVNfTElTVBAIGhCaph0MCgpmaWxlczpsaXN0EjYKH01BTklGRVNUX0NBUEFCSUxJVFlfRklMRVNfV1JJVEUQCRoRmqYdDQoLZmlsZXM6d3JpdGUSPgojTUFOSUZFU1RfQ0FQQUJJTElUWV9DVVNUT01FUlNfV1JJVEUQChoVmqYdEQoPY3VzdG9tZXJzOndyaXRlEkYKJ01BTklGRVNUX0NBUEFCSUxJVFlfTk9USUZJQ0FUSU9OU19XUklURRALGhmaph0VChNub3RpZmljYXRpb25zOndyaXRlEkoKKU1BTklGRVNUX0NBUEFCSUxJVFlfTk9USUZJQ0FUSU9OU19DT05TRU5UEAwaG5qmHRcKFW5vdGlmaWNhdGlvbnM6Y29uc2VudBI8CiJNQU5JRkVTVF9DQVBBQklMSVRZX0NVU1RPTUVSU19SRUFEEA4aFJqmHRAKDmN1c3RvbWVyczpyZWFkEkgKKE1BTklGRVNUX0NBUEFCSUxJVFlfQ1VTVE9NRVJTX1JFQURfTkFNRUQQDxoamqYdFgoUY3VzdG9tZXJzOnJlYWQ6bmFtZWQSTgorTUFOSUZFU1RfQ0FQQUJJTElUWV9DVVNUT01FUlNfUkVBRF9JREVOVElUWRAQGh2aph0ZChdjdXN0b21lcnM6cmVhZDppZGVudGl0eRJGCidNQU5JRkVTVF9DQVBBQklMSVRZX09QRVJBVElPTlNfQ09NUExFVEUQDRoZmqYdFQoTb3BlcmF0aW9uczpjb21wbGV0ZRI7CiBNQU5JRkVTVF9DQVBBQklMSVRZX1JFQ09SRFNfUkVBRBAUGhWaph0RCg1yZWNvcmRzOnJlYWQ6EAESPQohTUFOSUZFU1RfQ0FQQUJJTElUWV9SRUNPUkRTX1dSSVRFEBUaFpqmHRIKDnJlY29yZHM6d3JpdGU6EAESPwoiTUFOSUZFU1RfQ0FQQUJJTElUWV9SRUNPUkRTX0RFTEVURRAWGheaph0TCg9yZWNvcmRzOmRlbGV0ZToQARJBCiNNQU5JRkVTVF9DQVBBQklMSVRZX1JFQ09SRFNfUFVCTElTSBAXGhiaph0UChByZWNvcmRzOnB1Ymxpc2g6EAESRQolTUFOSUZFU1RfQ0FQQUJJTElUWV9SRUNPUkRTX0FUVFJJQlVURRAYGhqaph0WChJyZWNvcmRzOmF0dHJpYnV0ZToQATp1ChNtYW5pZmVzdF9jYXBhYmlsaXR5EiEuZ29vZ2xlLnByb3RvYnVmLkVudW1WYWx1ZU9wdGlvbnMY49QDIAEoCzIfLmFwcHMudjEuTWFuaWZlc3RDYXBhYmlsaXR5U3BlY1ISbWFuaWZlc3RDYXBhYmlsaXR5QjVaM2dpdGh1Yi5jb20vZWlkZXN0dWRpby9mb2lyL2dlbi9wcm90by9hcHBzL3YxO2FwcHN2MWIGcHJvdG8z", [file_buf_validate_validate, file_google_protobuf_descriptor2, file_google_protobuf_struct]);
12
12
  var AppConfigSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 0);
13
- var FeatureSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 1);
14
- var MetadataSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 2);
15
- var MiddlewareSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 3);
16
- var CredentialsSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 4);
17
- var Credentials_StrategySchema = /* @__PURE__ */ enumDesc2(file_apps_v1_apps, 4, 0);
13
+ var CustomerSignInSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 1);
14
+ var FeatureSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 2);
15
+ var MetadataSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 3);
16
+ var MiddlewareSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 4);
17
+ var CredentialsSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 5);
18
+ var Credentials_StrategySchema = /* @__PURE__ */ enumDesc2(file_apps_v1_apps, 5, 0);
18
19
  var Credentials_Strategy = /* @__PURE__ */ tsEnum2(Credentials_StrategySchema);
19
- var OAuthConfigSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 5);
20
- var InboundSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 6);
21
- var WebhookSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 7);
22
- var AccountWebhookSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 8);
23
- var SourceTypeSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 9);
24
- var SourceReferenceSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 10);
25
- var ReferenceRequestSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 11);
26
- var SinkContractSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 12);
27
- var SuggestedFieldSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 13);
28
- var SinkFieldSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 14);
29
- var OperationSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 15);
30
- var ManifestCapabilitySpecSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 16);
31
- var HookSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 17);
32
- var UISchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 18);
33
- var PlacementDeclarationSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 19);
34
- var PlacementDeclaration_TargetSchema = /* @__PURE__ */ enumDesc2(file_apps_v1_apps, 19, 0);
20
+ var OAuthConfigSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 6);
21
+ var InboundSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 7);
22
+ var WebhookSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 8);
23
+ var AccountWebhookSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 9);
24
+ var SourceTypeSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 10);
25
+ var SourceReferenceSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 11);
26
+ var ReferenceRequestSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 12);
27
+ var SinkContractSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 13);
28
+ var SuggestedFieldSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 14);
29
+ var SinkFieldSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 15);
30
+ var OperationSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 16);
31
+ var ManifestCapabilitySpecSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 17);
32
+ var HookSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 18);
33
+ var UISchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 19);
34
+ var PlacementDeclarationSchema = /* @__PURE__ */ messageDesc2(file_apps_v1_apps, 20);
35
+ var PlacementDeclaration_TargetSchema = /* @__PURE__ */ enumDesc2(file_apps_v1_apps, 20, 0);
35
36
  var PlacementDeclaration_Target = /* @__PURE__ */ tsEnum2(PlacementDeclaration_TargetSchema);
36
37
  var FieldSyncDirectionSchema = /* @__PURE__ */ enumDesc2(file_apps_v1_apps, 0);
37
38
  var FieldSyncDirection = /* @__PURE__ */ tsEnum2(FieldSyncDirectionSchema);
@@ -75,6 +76,7 @@ export {
75
76
  CredentialsSchema,
76
77
  Credentials_Strategy,
77
78
  Credentials_StrategySchema,
79
+ CustomerSignInSchema,
78
80
  DiscoveredAccountSchema,
79
81
  DiscoveredCountSchema,
80
82
  DiscoveredFieldGroupSchema,
package/dist/app.js CHANGED
@@ -251,6 +251,13 @@ function appCustomerUnlinkedOf(error) {
251
251
  if (typeof model !== "string" || typeof identityProvider !== "string") return null;
252
252
  return { model, identityProvider };
253
253
  }
254
+ function customerEmailUnavailableReasonOf(value) {
255
+ return typeof value === "string" && value !== "" ? value : null;
256
+ }
257
+ function appCustomerEmailUnavailableOf(error) {
258
+ const reason = customerEmailUnavailableReasonOf(extensionOf(error, "customerEmailUnavailable")?.reason);
259
+ return reason ? { reason } : null;
260
+ }
254
261
  function isAppFieldWithheld(error, field) {
255
262
  if (!(error instanceof FoirRequestError)) return false;
256
263
  return (error.prunedSelections ?? []).some((selection) => {
@@ -490,7 +497,7 @@ function createAppClient(config) {
490
497
  "integrationConfig",
491
498
  `query AppInstallConfig {
492
499
  integrationConfig {
493
- name enabled middlewareUrl targetModels webhookUrl config
500
+ name enabled middlewareUrl targetModels webhookUrl config customerEmailUnavailable customerSignIn
494
501
  sources { key model naturalKey mode fields { appField modelField direction } }
495
502
  sinks { key model naturalKey fields { appField modelField direction } }
496
503
  }
@@ -506,7 +513,9 @@ function createAppClient(config) {
506
513
  webhookUrl: got.webhookUrl ?? "",
507
514
  settings: got.config ?? {},
508
515
  sources: bindingsOf(got.sources),
509
- sinks: bindingsOf(got.sinks)
516
+ sinks: bindingsOf(got.sinks),
517
+ customerEmailUnavailable: customerEmailUnavailableReasonOf(got.customerEmailUnavailable),
518
+ customerSignIn: typeof got.customerSignIn === "string" && got.customerSignIn !== "" ? got.customerSignIn : null
510
519
  };
511
520
  },
512
521
  async credentials() {
@@ -724,16 +733,6 @@ function createAppClient(config) {
724
733
  customerDeleted: answer.customerDeleted === true,
725
734
  customerKeptBecause: answer.customerKeptBecause ?? null
726
735
  };
727
- },
728
- async identities(model, customerId) {
729
- if (customerId === "") refuse(`a customer's identities on ${model} were asked for with no customer`);
730
- const answer = await field(
731
- "customerIdentitiesById",
732
- `query AppCustomerIdentities($modelKey: String!, $customerId: ID!) { customerIdentitiesById(modelKey: $modelKey, customerId: $customerId) }`,
733
- { modelKey: model, customerId }
734
- );
735
- if (!Array.isArray(answer)) return refuse(`customerIdentitiesById reported no error and returned no list on ${model}`);
736
- return answer.filter((identity) => typeof identity === "string");
737
736
  }
738
737
  }
739
738
  },
@@ -768,6 +767,25 @@ function createAppClient(config) {
768
767
  );
769
768
  return customerOf(customer);
770
769
  },
770
+ async byIdentity(identity) {
771
+ if (identity === "") refuse("a customer was asked for with no identity");
772
+ const answer = await field(
773
+ "customerByIdentity",
774
+ `query AppCustomerByIdentity($identity: String!) { customerByIdentity(identity: $identity) }`,
775
+ { identity }
776
+ );
777
+ return typeof answer === "string" && answer !== "" ? answer : null;
778
+ },
779
+ async identities(customerId) {
780
+ if (customerId === "") refuse("a customer's identities were asked for with no customer");
781
+ const answer = await field(
782
+ "customerIdentitiesById",
783
+ `query AppCustomerIdentities($customerId: ID!) { customerIdentitiesById(customerId: $customerId) }`,
784
+ { customerId }
785
+ );
786
+ if (!Array.isArray(answer)) return refuse("customerIdentitiesById reported no error and returned no list");
787
+ return answer.filter((identity) => typeof identity === "string");
788
+ },
771
789
  async list(query = {}) {
772
790
  const search = query.search?.trim() ?? "";
773
791
  const page = await field(
@@ -1129,6 +1147,7 @@ var APP_CAPABILITIES = [
1129
1147
  "notifications:consent",
1130
1148
  "customers:read",
1131
1149
  "customers:read:named",
1150
+ "customers:read:identity",
1132
1151
  "operations:complete"
1133
1152
  ];
1134
1153
  var APP_RECORD_CAPABILITY_PREFIXES = [
@@ -1145,6 +1164,7 @@ export {
1145
1164
  FoirRequestError,
1146
1165
  MAX_APP_MANIFEST_BYTES,
1147
1166
  RECORD_KEY_RULE,
1167
+ appCustomerEmailUnavailableOf,
1148
1168
  appCustomerUnlinkedOf,
1149
1169
  appLookupConflictOf,
1150
1170
  appModelFieldName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foir/sdk",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Official Foir SDK — editor iframe client + server-side platform helpers",
5
5
  "type": "module",
6
6
  "publishConfig": {