@sixtyninecommerce/admin 0.8.0 → 0.10.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.
Files changed (179) hide show
  1. package/README.md +2739 -17
  2. package/dist/api-tokens/index.d.ts +15 -5
  3. package/dist/api-tokens/index.js +1 -0
  4. package/dist/apps/event-subscription.d.ts +59 -0
  5. package/dist/apps/event-subscription.js +32 -0
  6. package/dist/apps/hook-secret.d.ts +22 -0
  7. package/dist/apps/hook-secret.js +36 -0
  8. package/dist/apps/index.d.ts +54 -3
  9. package/dist/apps/index.js +17 -0
  10. package/dist/audit/index.d.ts +7 -0
  11. package/dist/auth/queries.d.ts +5 -1
  12. package/dist/auth/queries.js +6 -0
  13. package/dist/bulk/index.d.ts +323 -0
  14. package/dist/bulk/index.js +162 -0
  15. package/dist/catalog/assets.d.ts +147 -0
  16. package/dist/catalog/assets.js +164 -0
  17. package/dist/catalog/collections.d.ts +109 -0
  18. package/dist/catalog/collections.js +91 -0
  19. package/dist/catalog/facets.d.ts +103 -0
  20. package/dist/catalog/facets.js +100 -0
  21. package/dist/catalog/index.d.ts +115 -0
  22. package/dist/catalog/index.js +21 -0
  23. package/dist/catalog/options.d.ts +89 -0
  24. package/dist/catalog/options.js +88 -0
  25. package/dist/catalog/products-bulk.d.ts +21 -0
  26. package/dist/catalog/products-bulk.js +58 -0
  27. package/dist/catalog/products.d.ts +650 -0
  28. package/dist/catalog/products.js +458 -0
  29. package/dist/catalog/properties.d.ts +234 -0
  30. package/dist/catalog/properties.js +158 -0
  31. package/dist/catalog/types.d.ts +174 -0
  32. package/dist/catalog/types.js +141 -0
  33. package/dist/catalog/variants.d.ts +324 -0
  34. package/dist/catalog/variants.js +288 -0
  35. package/dist/catalog/visibility.d.ts +57 -0
  36. package/dist/catalog/visibility.js +59 -0
  37. package/dist/client.d.ts +192 -3
  38. package/dist/client.js +87 -12
  39. package/dist/compliance/index.d.ts +88 -0
  40. package/dist/compliance/index.js +88 -0
  41. package/dist/customer-data-redaction.d.ts +14 -0
  42. package/dist/customer-data-redaction.js +22 -0
  43. package/dist/customers/index.d.ts +428 -17
  44. package/dist/customers/index.js +322 -15
  45. package/dist/documents/index.d.ts +513 -16
  46. package/dist/documents/index.js +229 -3
  47. package/dist/events/index.d.ts +259 -0
  48. package/dist/events/index.js +147 -0
  49. package/dist/feeds/ai.d.ts +46 -0
  50. package/dist/feeds/ai.js +43 -0
  51. package/dist/feeds/categories.d.ts +50 -0
  52. package/dist/feeds/categories.js +56 -0
  53. package/dist/feeds/configs.d.ts +54 -0
  54. package/dist/feeds/configs.js +85 -0
  55. package/dist/feeds/identifier-fields.d.ts +25 -0
  56. package/dist/feeds/identifier-fields.js +29 -0
  57. package/dist/feeds/index.d.ts +54 -0
  58. package/dist/feeds/index.js +46 -0
  59. package/dist/feeds/preserve.d.ts +38 -0
  60. package/dist/feeds/preserve.js +74 -0
  61. package/dist/feeds/rules.d.ts +42 -0
  62. package/dist/feeds/rules.js +56 -0
  63. package/dist/feeds/types.d.ts +654 -0
  64. package/dist/feeds/types.js +135 -0
  65. package/dist/fulfillments/index.d.ts +437 -0
  66. package/dist/fulfillments/index.js +534 -0
  67. package/dist/heads/index.d.ts +60 -0
  68. package/dist/heads/index.js +45 -0
  69. package/dist/hooks/assignments.d.ts +90 -0
  70. package/dist/hooks/assignments.js +53 -0
  71. package/dist/hooks/index.d.ts +97 -0
  72. package/dist/hooks/index.js +76 -0
  73. package/dist/index.d.ts +95 -9
  74. package/dist/index.js +21 -1
  75. package/dist/integrations/index.d.ts +14 -0
  76. package/dist/integrations/index.js +9 -0
  77. package/dist/integrations/pipelines.d.ts +113 -0
  78. package/dist/integrations/pipelines.js +62 -0
  79. package/dist/integrations/types.d.ts +176 -0
  80. package/dist/integrations/types.js +8 -0
  81. package/dist/jobs/bulk-action-run.d.ts +74 -0
  82. package/dist/jobs/bulk-action-run.js +69 -0
  83. package/dist/locations/index.d.ts +6 -2
  84. package/dist/meta/index.d.ts +32 -0
  85. package/dist/orders/bulk.d.ts +75 -0
  86. package/dist/orders/bulk.js +150 -0
  87. package/dist/orders/commit-edit.d.ts +123 -0
  88. package/dist/orders/commit-edit.js +142 -0
  89. package/dist/orders/deposit-policy.d.ts +103 -0
  90. package/dist/orders/deposit-policy.js +202 -0
  91. package/dist/orders/get.d.ts +722 -0
  92. package/dist/orders/get.js +530 -0
  93. package/dist/orders/index.d.ts +186 -21
  94. package/dist/orders/index.js +57 -0
  95. package/dist/orders/line-price-preview.d.ts +297 -0
  96. package/dist/orders/line-price-preview.js +50 -0
  97. package/dist/orders/manual-discounts.d.ts +141 -0
  98. package/dist/orders/manual-discounts.js +97 -0
  99. package/dist/orders/manual-pricing.d.ts +192 -0
  100. package/dist/orders/manual-pricing.js +72 -0
  101. package/dist/orders/order-entry.d.ts +566 -0
  102. package/dist/orders/order-entry.js +129 -0
  103. package/dist/orders/parcel-charge.d.ts +45 -0
  104. package/dist/orders/parcel-charge.js +55 -0
  105. package/dist/orders/plan-edit.d.ts +489 -0
  106. package/dist/orders/plan-edit.js +93 -0
  107. package/dist/orders/status.d.ts +235 -0
  108. package/dist/orders/status.js +96 -0
  109. package/dist/orders/surcharges.d.ts +75 -2
  110. package/dist/orders/surcharges.js +110 -4
  111. package/dist/payments/account-credit.d.ts +171 -0
  112. package/dist/payments/account-credit.js +107 -0
  113. package/dist/payments/index.d.ts +241 -9
  114. package/dist/payments/index.js +92 -0
  115. package/dist/payments/methods.d.ts +37 -0
  116. package/dist/payments/methods.js +33 -0
  117. package/dist/platform/catalog.d.ts +148 -0
  118. package/dist/platform/catalog.js +74 -0
  119. package/dist/platform/entitlements.d.ts +111 -0
  120. package/dist/platform/entitlements.js +86 -0
  121. package/dist/platform/legal-entities.d.ts +139 -0
  122. package/dist/platform/legal-entities.js +126 -0
  123. package/dist/platform/legal-entity-numbering.d.ts +103 -0
  124. package/dist/platform/legal-entity-numbering.js +101 -0
  125. package/dist/platform/organization-membership.d.ts +80 -0
  126. package/dist/platform/organization-membership.js +71 -0
  127. package/dist/platform/organization-settings.d.ts +33 -0
  128. package/dist/platform/organization-settings.js +42 -0
  129. package/dist/platform/organization-sharing.d.ts +172 -0
  130. package/dist/platform/organization-sharing.js +120 -0
  131. package/dist/platform/organizations.d.ts +205 -0
  132. package/dist/platform/organizations.js +180 -0
  133. package/dist/platform/provision-store.d.ts +88 -8
  134. package/dist/platform/provision-store.js +12 -0
  135. package/dist/platform/reports.d.ts +46 -0
  136. package/dist/platform/reports.js +31 -0
  137. package/dist/platform/server-metrics.d.ts +58 -0
  138. package/dist/platform/server-metrics.js +11 -0
  139. package/dist/pos/index.d.ts +567 -0
  140. package/dist/pos/index.js +205 -0
  141. package/dist/pricing/index.d.ts +408 -0
  142. package/dist/pricing/index.js +353 -0
  143. package/dist/pricing/vouchers.d.ts +103 -0
  144. package/dist/pricing/vouchers.js +69 -0
  145. package/dist/provenance/index.d.ts +93 -0
  146. package/dist/provenance/index.js +53 -0
  147. package/dist/reports/index.d.ts +199 -0
  148. package/dist/reports/index.js +154 -0
  149. package/dist/returns/index.d.ts +225 -0
  150. package/dist/returns/index.js +160 -0
  151. package/dist/search/index.d.ts +99 -0
  152. package/dist/search/index.js +62 -0
  153. package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
  154. package/dist/shipping/parcel-count-suggestion.js +73 -0
  155. package/dist/shipping/parcel-rule.d.ts +48 -0
  156. package/dist/shipping/parcel-rule.js +91 -0
  157. package/dist/stock/index.d.ts +166 -0
  158. package/dist/stock/index.js +115 -0
  159. package/dist/stock/transfers.d.ts +520 -0
  160. package/dist/stock/transfers.js +437 -0
  161. package/dist/stored-value/index.d.ts +154 -0
  162. package/dist/stored-value/index.js +130 -0
  163. package/dist/tenant/capabilities.d.ts +10 -0
  164. package/dist/tenant/entitlements.d.ts +35 -0
  165. package/dist/tenant/entitlements.js +15 -0
  166. package/dist/tenant/field-groups.d.ts +64 -0
  167. package/dist/tenant/field-groups.js +34 -0
  168. package/dist/tenant/legal-entity.d.ts +32 -0
  169. package/dist/tenant/legal-entity.js +41 -0
  170. package/dist/tenant/notifications.d.ts +148 -0
  171. package/dist/tenant/notifications.js +79 -0
  172. package/dist/tenant/retention.d.ts +10 -4
  173. package/dist/tenant/settings-sources.d.ts +103 -0
  174. package/dist/tenant/settings-sources.js +92 -0
  175. package/dist/types.d.ts +86 -11
  176. package/dist/uploads/index.d.ts +11 -7
  177. package/dist/users/index.d.ts +104 -0
  178. package/dist/users/index.js +80 -0
  179. package/package.json +3 -2
@@ -1,10 +1,10 @@
1
1
  import { type Page, type Transport } from '@sixtyninecommerce/core';
2
2
  import type { ApiToken, Call, CreateApiTokenInput, ListOptions, UpdateApiTokenInput } from '../types.js';
3
- export declare const API_TOKEN_FIELDS = "\n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n";
4
- export declare const LIST_QUERY = "\n query ApiTokens($options: ApiTokenListOptions) {\n apiTokens(options: $options) {\n totalItems\n items { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n }\n }\n }\n";
5
- export declare const CREATE_MUTATION = "\n mutation CreateApiToken($input: CreateApiTokenInput!) {\n createApiToken(input: $input) {\n token\n apiToken { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n }\n }\n }\n";
6
- export declare const UPDATE_MUTATION = "\n mutation UpdateApiToken($input: UpdateApiTokenInput!) {\n updateApiToken(input: $input) { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n }\n }\n";
7
- export declare const REVOKE_MUTATION = "\n mutation RevokeApiToken($id: ID!) {\n revokeApiToken(id: $id) { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n }\n }\n";
3
+ export declare const API_TOKEN_FIELDS = "\n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n fieldOwnership\n";
4
+ export declare const LIST_QUERY = "\n query ApiTokens($options: ApiTokenListOptions) {\n apiTokens(options: $options) {\n totalItems\n items { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n fieldOwnership\n }\n }\n }\n";
5
+ export declare const CREATE_MUTATION = "\n mutation CreateApiToken($input: CreateApiTokenInput!) {\n createApiToken(input: $input) {\n token\n apiToken { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n fieldOwnership\n }\n }\n }\n";
6
+ export declare const UPDATE_MUTATION = "\n mutation UpdateApiToken($input: UpdateApiTokenInput!) {\n updateApiToken(input: $input) { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n fieldOwnership\n }\n }\n";
7
+ export declare const REVOKE_MUTATION = "\n mutation RevokeApiToken($id: ID!) {\n revokeApiToken(id: $id) { \n id\n name\n tokenPrefix\n permissions\n channelId\n locationIds\n expiresAt\n lastUsedAt\n revokedAt\n createdAt\n fieldOwnership\n }\n }\n";
8
8
  export declare const DELETE_MUTATION = "\n mutation DeleteApiTokens($ids: [ID!]!) {\n deleteApiTokens(ids: $ids) {\n result\n message\n }\n }\n";
9
9
  /** The raw token is shown exactly once, at creation. */
10
10
  export interface CreatedApiToken {
@@ -21,7 +21,17 @@ export interface CreatedApiToken {
21
21
  */
22
22
  export interface ApiTokensModule {
23
23
  list(options?: ListOptions): Promise<Page<ApiToken>>;
24
+ /**
25
+ * Mint a token. `fieldOwnership` hands it field groups (see `ApiToken.fieldOwnership`): the
26
+ * engine refuses a group the token could not change, one you cannot change yourself, one
27
+ * another app or token owns on the store, or one Exact or the store's organization writes
28
+ * (`FieldGroupGrantRefusalCode`), before anything is minted.
29
+ */
24
30
  create(input: CreateApiTokenInput, options?: Call): Promise<CreatedApiToken>;
31
+ /**
32
+ * Edit a live token. `fieldOwnership` omitted or null keeps its groups (dropping any its new
33
+ * permissions cannot change); a list replaces them, judging only the groups it adds.
34
+ */
25
35
  update(id: string, input: UpdateApiTokenInput, options?: Call): Promise<ApiToken>;
26
36
  revoke(id: string, options?: Call): Promise<ApiToken>;
27
37
  delete(ids: string[], options?: Call): Promise<void>;
@@ -10,6 +10,7 @@ export const API_TOKEN_FIELDS = /* GraphQL */ `
10
10
  lastUsedAt
11
11
  revokedAt
12
12
  createdAt
13
+ fieldOwnership
13
14
  `;
14
15
  export const LIST_QUERY = /* GraphQL */ `
15
16
  query ApiTokens($options: ApiTokenListOptions) {
@@ -0,0 +1,59 @@
1
+ import { type Transport } from '@sixtyninecommerce/core';
2
+ import type { RotatedEventSubscriptionSecret } from '../events/index.js';
3
+ import type { Call } from '../types.js';
4
+ /**
5
+ * The calling installation's own event subscription (headless spec §6.9 "App
6
+ * subscriptions").
7
+ *
8
+ * Installing an app that declares `events.endpoint` creates it; the platform
9
+ * then POSTs the app's declared events there, signed. The app FETCHES its
10
+ * signing secret — it is never pushed to the endpoint, because anyone can POST
11
+ * to a public URL and a pushed secret is one an attacker could plant.
12
+ */
13
+ export interface AppEventSubscription {
14
+ id: string;
15
+ /** The rendered endpoint deliveries go to. */
16
+ url: string;
17
+ eventFilters: string[];
18
+ enabled: boolean;
19
+ disabledReason: string | null;
20
+ /**
21
+ * True when the app declares `customerData: none`: the customer's name and
22
+ * email are then withheld (`customer.redacted: true`, the id kept). False at
23
+ * `identity` and `full`, whose deliveries carry them. An erased customer's
24
+ * are blank at every level (`customer.erased: true`).
25
+ */
26
+ redactCustomerIdentity: boolean;
27
+ deliveryScope: string;
28
+ createdAt: string;
29
+ /** When the secret was taken. Null on the one answer that carries it. */
30
+ secretFetchedAt: string | null;
31
+ /**
32
+ * The signing secret (hex), on the FIRST read only — store it then. Every later
33
+ * read omits it; a lost secret is replaced with `rotateSecret()`. Pass it to
34
+ * `createEventHandler` / `verifyEventDelivery`.
35
+ */
36
+ secret?: string;
37
+ /** The same key as a Standard Webhooks `whsec_` secret. First read only. */
38
+ secretStandard?: string;
39
+ [extra: string]: unknown;
40
+ }
41
+ export interface AppEventSubscriptionModule {
42
+ /**
43
+ * Read it. Needs the INSTALLATION's own credential (`auth: { app, installationId }`)
44
+ * and answers `NotFoundError` (`EVENT_SUBSCRIPTION_NOT_FOUND`) when the app
45
+ * declares no events, waits for consent, or is not active. Takes no idempotency
46
+ * key: the first answer carries the secret, which the platform never stores
47
+ * for replay.
48
+ */
49
+ (options?: Omit<Call, 'idempotencyKey'>): Promise<AppEventSubscription>;
50
+ /**
51
+ * Issue a new signing secret and answer it ONCE; the old one keeps co-signing
52
+ * for 24 hours. Same rules as `events.subscriptions.rotateSecret`, including
53
+ * `force` and the refusal of a second rotation inside the overlap.
54
+ */
55
+ rotateSecret(options?: {
56
+ force?: boolean;
57
+ } & Omit<Call, 'idempotencyKey'>): Promise<RotatedEventSubscriptionSecret>;
58
+ }
59
+ export declare function createAppEventSubscriptionModule(transport: Transport): AppEventSubscriptionModule;
@@ -0,0 +1,32 @@
1
+ import { ValidationError } from '@sixtyninecommerce/core';
2
+ function refuseIdempotencyKey(options, what) {
3
+ if (options && 'idempotencyKey' in options && options.idempotencyKey !== undefined) {
4
+ throw new ValidationError({
5
+ code: 'IDEMPOTENCY_NOT_SUPPORTED',
6
+ message: `${what} takes no idempotency key: its answer carries a signing secret, which the platform never stores for replay.`,
7
+ status: 400,
8
+ service: 'plugins',
9
+ });
10
+ }
11
+ }
12
+ export function createAppEventSubscriptionModule(transport) {
13
+ const read = async (options) => {
14
+ refuseIdempotencyKey(options, 'apps.installation.eventSubscription');
15
+ // ONE attempt, although it is a GET: the first answer spends the secret on
16
+ // the server, so an automatic retry after a lost response would come back
17
+ // "successfully" without it. A caller that sees the error reads again, and
18
+ // if `secretFetchedAt` is then set, calls `rotateSecret()`.
19
+ return transport.rest('plugins', { method: 'GET', path: '/event-subscriptions/installation' }, { ...options, retry: { attempts: 1 } });
20
+ };
21
+ return Object.assign(read, {
22
+ async rotateSecret(options) {
23
+ refuseIdempotencyKey(options, 'apps.installation.eventSubscription.rotateSecret');
24
+ const { force, ...call } = options ?? {};
25
+ return transport.rest('plugins', {
26
+ method: 'POST',
27
+ path: '/event-subscriptions/installation/rotate-secret',
28
+ query: { force: force === true ? true : undefined },
29
+ }, call);
30
+ },
31
+ });
32
+ }
@@ -0,0 +1,22 @@
1
+ import { type Transport } from '@sixtyninecommerce/core';
2
+ import type { Call } from '../types.js';
3
+ /** The calling installation's hook signing secret (headless spec §8.14, user decision E6). */
4
+ export interface AppHookSecret {
5
+ installationId: string;
6
+ /** Standard Webhooks `whsec_…`: pass it to `createHookHandler({ secret })`. */
7
+ secret: string;
8
+ /** ISO-8601 with offset: when it was taken. */
9
+ fetchedAt: string;
10
+ }
11
+ export type AppHookSecretModule = (options?: Omit<Call, 'idempotencyKey'>) => Promise<AppHookSecret>;
12
+ /**
13
+ * Read the calling installation's hook secret — ONCE. Needs the installation's
14
+ * own credential (`auth: { app, installationId }`); any other principal is
15
+ * refused `HOOK_SECRET_NOT_ALLOWED`. Store it: every later read is refused
16
+ * `HOOK_SECRET_ALREADY_FETCHED` (a `ConflictError`), and `HOOK_SECRET_NOT_FOUND`
17
+ * means the app declares no hooks or the merchant has not approved them yet.
18
+ *
19
+ * The platform signs every hook call to this installation with it; it is never
20
+ * pushed to your URL, because anyone can POST to a public URL.
21
+ */
22
+ export declare function createAppHookSecretModule(transport: Transport): AppHookSecretModule;
@@ -0,0 +1,36 @@
1
+ import { ValidationError } from '@sixtyninecommerce/core';
2
+ const FETCH_HOOK_SECRET = /* GraphQL */ `
3
+ mutation FetchAppHookSecret {
4
+ fetchAppHookSecret {
5
+ installationId
6
+ secret
7
+ fetchedAt
8
+ }
9
+ }
10
+ `;
11
+ /**
12
+ * Read the calling installation's hook secret — ONCE. Needs the installation's
13
+ * own credential (`auth: { app, installationId }`); any other principal is
14
+ * refused `HOOK_SECRET_NOT_ALLOWED`. Store it: every later read is refused
15
+ * `HOOK_SECRET_ALREADY_FETCHED` (a `ConflictError`), and `HOOK_SECRET_NOT_FOUND`
16
+ * means the app declares no hooks or the merchant has not approved them yet.
17
+ *
18
+ * The platform signs every hook call to this installation with it; it is never
19
+ * pushed to your URL, because anyone can POST to a public URL.
20
+ */
21
+ export function createAppHookSecretModule(transport) {
22
+ return async (options) => {
23
+ if (options && 'idempotencyKey' in options && options.idempotencyKey !== undefined) {
24
+ throw new ValidationError({
25
+ code: 'IDEMPOTENCY_NOT_SUPPORTED',
26
+ message: 'apps.installation.hookSecret takes no idempotency key: its answer carries a signing secret, which the platform never stores for replay.',
27
+ status: 400,
28
+ service: 'vendure',
29
+ });
30
+ }
31
+ // ONE attempt: the first answer spends the secret on the server, so an
32
+ // automatic retry after a lost response would come back "already fetched".
33
+ const data = await transport.graphql({ api: 'admin', query: FETCH_HOOK_SECRET, variables: {} }, { ...options, retry: { attempts: 1 } });
34
+ return data.fetchAppHookSecret;
35
+ };
36
+ }
@@ -1,4 +1,5 @@
1
1
  import type { Transport } from '@sixtyninecommerce/core';
2
+ import { type FieldGroupOwnership } from '../tenant/field-groups.js';
2
3
  import type { Call } from '../types.js';
3
4
  export declare const APP_FIELDS = "\n id\n slug\n name\n ownerLabel\n declaredPermissions\n declaredEvents\n declaredFieldOwnership\n customerData\n customerDataPurpose\n metafieldDefinitions\n configSchema\n hooks\n headTemplates\n sandboxOnly\n status\n manifestVersion\n createdAt\n updatedAt\n";
4
5
  export declare const INSTALLATION_FIELDS = "\n id\n appId\n targetKind\n targetId\n grantedPermissions\n grantedFieldOwnership\n locationIds\n status\n consentVersion\n pendingConsentReasons\n installedAt\n uninstalledAt\n";
@@ -7,6 +8,7 @@ export declare const MY_INSTALLATIONS_QUERY = "\n query MyInstallations {\n
7
8
  export declare const INSTALLATION_QUERY = "\n query AppInstallation($id: ID!) {\n appInstallation(id: $id) { \n id\n appId\n targetKind\n targetId\n grantedPermissions\n grantedFieldOwnership\n locationIds\n status\n consentVersion\n pendingConsentReasons\n installedAt\n uninstalledAt\n }\n }\n";
8
9
  export declare const INSTALL_MUTATION = "\n mutation InstallApp($input: InstallAppInput!) {\n installApp(input: $input) { \n id\n appId\n targetKind\n targetId\n grantedPermissions\n grantedFieldOwnership\n locationIds\n status\n consentVersion\n pendingConsentReasons\n installedAt\n uninstalledAt\n }\n }\n";
9
10
  export declare const APPROVE_MUTATION = "\n mutation ApproveInstallationConsent($id: ID!, $input: ApproveInstallationConsentInput) {\n approveInstallationConsent(id: $id, input: $input) { \n id\n appId\n targetKind\n targetId\n grantedPermissions\n grantedFieldOwnership\n locationIds\n status\n consentVersion\n pendingConsentReasons\n installedAt\n uninstalledAt\n }\n }\n";
11
+ export declare const RELEASE_FIELD_GROUP_MUTATION = "\n mutation ReleaseFieldGroup($input: ReleaseFieldGroupInput!, $idempotencyKey: String) {\n releaseFieldGroup(input: $input, idempotencyKey: $idempotencyKey) { \n group\n ownerKind\n ownerId\n label\n appSlug\n paused\n }\n }\n";
10
12
  export declare const UNINSTALL_MUTATION = "\n mutation UninstallApp($id: ID!) {\n uninstallApp(id: $id) { \n id\n appId\n targetKind\n targetId\n grantedPermissions\n grantedFieldOwnership\n locationIds\n status\n consentVersion\n pendingConsentReasons\n installedAt\n uninstalledAt\n }\n }\n";
11
13
  /** A registered app, as a tenant admin sees it on the consent screen. */
12
14
  export interface App {
@@ -17,7 +19,11 @@ export interface App {
17
19
  /** The ceiling on what an installation may be granted. */
18
20
  declaredPermissions: string[];
19
21
  declaredEvents: string[];
20
- /** Normalised `"<entity>.<field>"` claims, e.g. `product.price`. */
22
+ /**
23
+ * The field groups the app asks to own, by key: `price` (Price), `stock` (Stock),
24
+ * `product_details` (Product details), `customer_details` (Customer details). Each is a fixed
25
+ * list of fields the platform defines; an app never claims a single field.
26
+ */
21
27
  declaredFieldOwnership: string[];
22
28
  /** `none` | `identity` | `full`. */
23
29
  customerData: string;
@@ -26,6 +32,7 @@ export interface App {
26
32
  configSchema: Record<string, unknown> | null;
27
33
  hooks: unknown[];
28
34
  headTemplates: unknown[];
35
+ /** Installable on a sandbox store only (`SANDBOX_ONLY_APP` elsewhere). */
29
36
  sandboxOnly: boolean;
30
37
  /** `active` | `suspended`. */
31
38
  status: string;
@@ -40,7 +47,7 @@ export interface AppInstallation {
40
47
  targetKind: string;
41
48
  targetId: string;
42
49
  grantedPermissions: string[];
43
- /** Normalised `"<entity>.<field>"` claims, e.g. `product.price`. */
50
+ /** The field groups this installation was granted, by key (see `App.declaredFieldOwnership`). */
44
51
  grantedFieldOwnership: string[];
45
52
  /** Null means unrestricted. */
46
53
  locationIds: string[] | null;
@@ -66,6 +73,13 @@ export interface ApproveConsentInput {
66
73
  */
67
74
  acknowledgedConsentReasons?: string[];
68
75
  grantedPermissions?: string[];
76
+ /**
77
+ * Replaces the granted field groups, by key (`price`, `stock`, `product_details`,
78
+ * `customer_details`), each one the app declares. Any other value is refused with
79
+ * `FIELD_GROUP_INVALID` (a `ValidationError`). A group the installation does not own yet is
80
+ * judged as on `install` (`FieldGroupGrantRefusalCode`); one it owns already is kept. Omit it
81
+ * to keep the current set: a group the approved permissions cannot change is then dropped.
82
+ */
69
83
  grantedFieldOwnership?: string[];
70
84
  /**
71
85
  * Replace the stock-location restriction. Omit to keep it; pass `null` to LIFT
@@ -75,10 +89,24 @@ export interface ApproveConsentInput {
75
89
  */
76
90
  locationIds?: string[] | null;
77
91
  }
92
+ /** The owner a release names: an app installation or a personal API token of this store. */
93
+ export interface FieldGroupOwnerRef {
94
+ kind: 'app_installation' | 'api_token';
95
+ /** The installation id or the API token id. */
96
+ id: string;
97
+ }
78
98
  export interface InstallAppInput {
79
99
  appId: string;
80
100
  /** Must be a subset of the app's `declaredPermissions`, and of the caller's own. */
81
101
  grantedPermissions?: string[];
102
+ /**
103
+ * Field groups to grant, by key (`price`, `stock`, `product_details`, `customer_details`), a
104
+ * subset of the app's `declaredFieldOwnership`. Any other value is refused with
105
+ * `FIELD_GROUP_INVALID` (a `ValidationError`). The store's rules apply too: the app must be
106
+ * granted a permission that changes each group, you must be able to change it yourself, no
107
+ * other app or token may own it here, and neither Exact nor the store's organization may
108
+ * write it (`FieldGroupGrantRefusalCode`).
109
+ */
82
110
  grantedFieldOwnership?: string[];
83
111
  /** Restrict the installation to these stock locations. Omit for unrestricted. */
84
112
  locationIds?: string[];
@@ -97,7 +125,13 @@ export interface InstallAppInput {
97
125
  export interface AppsModule {
98
126
  /** Apps a platform operator has enabled for this store and that are not suspended. */
99
127
  list(options?: Call): Promise<App[]>;
100
- /** Consent to a subset of an app's declarations and install it. */
128
+ /**
129
+ * Consent to a subset of an app's declarations and install it.
130
+ *
131
+ * An app with `sandboxOnly: true` installs on a sandbox store only
132
+ * (`platform.provisionStore({ sandbox: true })`); on any other store the
133
+ * engine refuses it with `SANDBOX_ONLY_APP`.
134
+ */
101
135
  install(input: InstallAppInput, options?: Call): Promise<AppInstallation>;
102
136
  /** This store's installations, newest first. */
103
137
  installations(options?: Call): Promise<AppInstallation[]>;
@@ -113,5 +147,22 @@ export interface AppsModule {
113
147
  approveConsent(id: string, grants?: ApproveConsentInput, options?: Call): Promise<AppInstallation>;
114
148
  /** Revoke every token and end the grant. The row is kept as provenance. */
115
149
  uninstall(id: string, options?: Call): Promise<AppInstallation>;
150
+ /**
151
+ * Release one field group from one owner on this store, in one recorded step: the group comes
152
+ * off the installation's `grantedFieldOwnership` or the API token's `fieldOwnership`, the
153
+ * engine audits it and announces `app.field_ownership_changed`, and staff, imports and other
154
+ * apps can be granted it or change that data again. The owner keeps everything else.
155
+ *
156
+ * Releasing an installation's group needs `InstallApps`, a token's `ManageApiToken`
157
+ * (`PermissionDeniedError FORBIDDEN` otherwise). Refused `FIELD_GROUP_NOT_HELD`
158
+ * (`BusinessRuleError`) when the owner does not own the group — an uninstalled installation
159
+ * and a revoked or expired token own none — and `ENTITY_NOT_FOUND` (`NotFoundError`) for an
160
+ * owner on another store.
161
+ *
162
+ * `options.idempotencyKey` is honoured: a retry under the same key within 24 h answers the
163
+ * first release instead of releasing again (a repeat WITHOUT the key answers
164
+ * `FIELD_GROUP_NOT_HELD`).
165
+ */
166
+ releaseFieldGroup(owner: FieldGroupOwnerRef, group: string, options?: Call): Promise<FieldGroupOwnership>;
116
167
  }
117
168
  export declare function createAppsModule(transport: Transport): AppsModule;
@@ -1,3 +1,4 @@
1
+ import { FIELD_GROUP_OWNERSHIP_FIELDS } from '../tenant/field-groups.js';
1
2
  export const APP_FIELDS = /* GraphQL */ `
2
3
  id
3
4
  slug
@@ -57,6 +58,11 @@ export const APPROVE_MUTATION = /* GraphQL */ `
57
58
  approveInstallationConsent(id: $id, input: $input) { ${INSTALLATION_FIELDS} }
58
59
  }
59
60
  `;
61
+ export const RELEASE_FIELD_GROUP_MUTATION = /* GraphQL */ `
62
+ mutation ReleaseFieldGroup($input: ReleaseFieldGroupInput!, $idempotencyKey: String) {
63
+ releaseFieldGroup(input: $input, idempotencyKey: $idempotencyKey) { ${FIELD_GROUP_OWNERSHIP_FIELDS} }
64
+ }
65
+ `;
60
66
  export const UNINSTALL_MUTATION = /* GraphQL */ `
61
67
  mutation UninstallApp($id: ID!) {
62
68
  uninstallApp(id: $id) { ${INSTALLATION_FIELDS} }
@@ -84,6 +90,17 @@ export function createAppsModule(transport) {
84
90
  const data = await transport.graphql({ api: 'admin', query: APPROVE_MUTATION, variables: { id, input: grants ?? null } }, options);
85
91
  return data.approveInstallationConsent;
86
92
  },
93
+ async releaseFieldGroup(owner, group, options) {
94
+ const data = await transport.graphql({
95
+ api: 'admin',
96
+ query: RELEASE_FIELD_GROUP_MUTATION,
97
+ variables: {
98
+ input: { ownerKind: owner.kind, ownerId: owner.id, group },
99
+ idempotencyKey: options?.idempotencyKey,
100
+ },
101
+ }, options);
102
+ return data.releaseFieldGroup;
103
+ },
87
104
  async uninstall(id, options) {
88
105
  const data = await transport.graphql({ api: 'admin', query: UNINSTALL_MUTATION, variables: { id } }, options);
89
106
  return data.uninstallApp;
@@ -150,6 +150,13 @@ export interface AuditCorrelation {
150
150
  * reads at once rather than up to a permission-cache later. That costs one
151
151
  * extra round trip per call, which is why these are poller routes, not
152
152
  * something to put behind a table a browser repaints.
153
+ *
154
+ * **An app installation below `customerData: full`** reads the same rows with
155
+ * every field that can carry customer data null — `actorLabel`, `targetLabel`,
156
+ * `errorDetail`, `metadata`, and a call's `endpoint`, `requestBody`,
157
+ * `responseBody` and `error` — and a text search (`q`) is refused with
158
+ * `PermissionDeniedError` code `CUSTOMER_DATA_NOT_GRANTED`. Ids, codes,
159
+ * outcomes and moments stay.
153
160
  */
154
161
  export interface AuditModule {
155
162
  events(filter?: AuditEventFilter): Promise<Page<AuditEvent>>;
@@ -7,5 +7,9 @@
7
7
  * header, which the transport hands to `Principal.onToken`.
8
8
  */
9
9
  export declare const LOGIN_MUTATION = "\n mutation Login($username: String!, $password: String!) {\n login(username: $username, password: $password) {\n __typename\n ... on CurrentUser {\n id\n }\n ... on ErrorResult {\n errorCode\n message\n }\n }\n }\n";
10
- export declare const ME_QUERY = "\n query Me {\n me {\n id\n identifier\n channels {\n id\n code\n token\n permissions\n }\n }\n activeAdministrator {\n id\n firstName\n lastName\n emailAddress\n }\n }\n";
10
+ /**
11
+ * `channelKind` / `organizationId` need a platform release that serves them
12
+ * (#812); against an older engine this document fails validation as a whole.
13
+ */
14
+ export declare const ME_QUERY = "\n query Me {\n me {\n id\n identifier\n channels {\n id\n code\n token\n permissions\n channelKind\n organizationId\n }\n }\n activeAdministrator {\n id\n firstName\n lastName\n emailAddress\n }\n }\n";
11
15
  export declare const LOGOUT_MUTATION = "\n mutation Logout {\n logout {\n success\n }\n }\n";
@@ -20,6 +20,10 @@ export const LOGIN_MUTATION = /* GraphQL */ `
20
20
  }
21
21
  }
22
22
  `;
23
+ /**
24
+ * `channelKind` / `organizationId` need a platform release that serves them
25
+ * (#812); against an older engine this document fails validation as a whole.
26
+ */
23
27
  export const ME_QUERY = /* GraphQL */ `
24
28
  query Me {
25
29
  me {
@@ -30,6 +34,8 @@ export const ME_QUERY = /* GraphQL */ `
30
34
  code
31
35
  token
32
36
  permissions
37
+ channelKind
38
+ organizationId
33
39
  }
34
40
  }
35
41
  activeAdministrator {