@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.
- package/README.md +2739 -17
- package/dist/api-tokens/index.d.ts +15 -5
- package/dist/api-tokens/index.js +1 -0
- package/dist/apps/event-subscription.d.ts +59 -0
- package/dist/apps/event-subscription.js +32 -0
- package/dist/apps/hook-secret.d.ts +22 -0
- package/dist/apps/hook-secret.js +36 -0
- package/dist/apps/index.d.ts +54 -3
- package/dist/apps/index.js +17 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/auth/queries.d.ts +5 -1
- package/dist/auth/queries.js +6 -0
- package/dist/bulk/index.d.ts +323 -0
- package/dist/bulk/index.js +162 -0
- package/dist/catalog/assets.d.ts +147 -0
- package/dist/catalog/assets.js +164 -0
- package/dist/catalog/collections.d.ts +109 -0
- package/dist/catalog/collections.js +91 -0
- package/dist/catalog/facets.d.ts +103 -0
- package/dist/catalog/facets.js +100 -0
- package/dist/catalog/index.d.ts +115 -0
- package/dist/catalog/index.js +21 -0
- package/dist/catalog/options.d.ts +89 -0
- package/dist/catalog/options.js +88 -0
- package/dist/catalog/products-bulk.d.ts +21 -0
- package/dist/catalog/products-bulk.js +58 -0
- package/dist/catalog/products.d.ts +650 -0
- package/dist/catalog/products.js +458 -0
- package/dist/catalog/properties.d.ts +234 -0
- package/dist/catalog/properties.js +158 -0
- package/dist/catalog/types.d.ts +174 -0
- package/dist/catalog/types.js +141 -0
- package/dist/catalog/variants.d.ts +324 -0
- package/dist/catalog/variants.js +288 -0
- package/dist/catalog/visibility.d.ts +57 -0
- package/dist/catalog/visibility.js +59 -0
- package/dist/client.d.ts +192 -3
- package/dist/client.js +87 -12
- package/dist/compliance/index.d.ts +88 -0
- package/dist/compliance/index.js +88 -0
- package/dist/customer-data-redaction.d.ts +14 -0
- package/dist/customer-data-redaction.js +22 -0
- package/dist/customers/index.d.ts +428 -17
- package/dist/customers/index.js +322 -15
- package/dist/documents/index.d.ts +513 -16
- package/dist/documents/index.js +229 -3
- package/dist/events/index.d.ts +259 -0
- package/dist/events/index.js +147 -0
- package/dist/feeds/ai.d.ts +46 -0
- package/dist/feeds/ai.js +43 -0
- package/dist/feeds/categories.d.ts +50 -0
- package/dist/feeds/categories.js +56 -0
- package/dist/feeds/configs.d.ts +54 -0
- package/dist/feeds/configs.js +85 -0
- package/dist/feeds/identifier-fields.d.ts +25 -0
- package/dist/feeds/identifier-fields.js +29 -0
- package/dist/feeds/index.d.ts +54 -0
- package/dist/feeds/index.js +46 -0
- package/dist/feeds/preserve.d.ts +38 -0
- package/dist/feeds/preserve.js +74 -0
- package/dist/feeds/rules.d.ts +42 -0
- package/dist/feeds/rules.js +56 -0
- package/dist/feeds/types.d.ts +654 -0
- package/dist/feeds/types.js +135 -0
- package/dist/fulfillments/index.d.ts +437 -0
- package/dist/fulfillments/index.js +534 -0
- package/dist/heads/index.d.ts +60 -0
- package/dist/heads/index.js +45 -0
- package/dist/hooks/assignments.d.ts +90 -0
- package/dist/hooks/assignments.js +53 -0
- package/dist/hooks/index.d.ts +97 -0
- package/dist/hooks/index.js +76 -0
- package/dist/index.d.ts +95 -9
- package/dist/index.js +21 -1
- package/dist/integrations/index.d.ts +14 -0
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/pipelines.d.ts +113 -0
- package/dist/integrations/pipelines.js +62 -0
- package/dist/integrations/types.d.ts +176 -0
- package/dist/integrations/types.js +8 -0
- package/dist/jobs/bulk-action-run.d.ts +74 -0
- package/dist/jobs/bulk-action-run.js +69 -0
- package/dist/locations/index.d.ts +6 -2
- package/dist/meta/index.d.ts +32 -0
- package/dist/orders/bulk.d.ts +75 -0
- package/dist/orders/bulk.js +150 -0
- package/dist/orders/commit-edit.d.ts +123 -0
- package/dist/orders/commit-edit.js +142 -0
- package/dist/orders/deposit-policy.d.ts +103 -0
- package/dist/orders/deposit-policy.js +202 -0
- package/dist/orders/get.d.ts +722 -0
- package/dist/orders/get.js +530 -0
- package/dist/orders/index.d.ts +186 -21
- package/dist/orders/index.js +57 -0
- package/dist/orders/line-price-preview.d.ts +297 -0
- package/dist/orders/line-price-preview.js +50 -0
- package/dist/orders/manual-discounts.d.ts +141 -0
- package/dist/orders/manual-discounts.js +97 -0
- package/dist/orders/manual-pricing.d.ts +192 -0
- package/dist/orders/manual-pricing.js +72 -0
- package/dist/orders/order-entry.d.ts +566 -0
- package/dist/orders/order-entry.js +129 -0
- package/dist/orders/parcel-charge.d.ts +45 -0
- package/dist/orders/parcel-charge.js +55 -0
- package/dist/orders/plan-edit.d.ts +489 -0
- package/dist/orders/plan-edit.js +93 -0
- package/dist/orders/status.d.ts +235 -0
- package/dist/orders/status.js +96 -0
- package/dist/orders/surcharges.d.ts +75 -2
- package/dist/orders/surcharges.js +110 -4
- package/dist/payments/account-credit.d.ts +171 -0
- package/dist/payments/account-credit.js +107 -0
- package/dist/payments/index.d.ts +241 -9
- package/dist/payments/index.js +92 -0
- package/dist/payments/methods.d.ts +37 -0
- package/dist/payments/methods.js +33 -0
- package/dist/platform/catalog.d.ts +148 -0
- package/dist/platform/catalog.js +74 -0
- package/dist/platform/entitlements.d.ts +111 -0
- package/dist/platform/entitlements.js +86 -0
- package/dist/platform/legal-entities.d.ts +139 -0
- package/dist/platform/legal-entities.js +126 -0
- package/dist/platform/legal-entity-numbering.d.ts +103 -0
- package/dist/platform/legal-entity-numbering.js +101 -0
- package/dist/platform/organization-membership.d.ts +80 -0
- package/dist/platform/organization-membership.js +71 -0
- package/dist/platform/organization-settings.d.ts +33 -0
- package/dist/platform/organization-settings.js +42 -0
- package/dist/platform/organization-sharing.d.ts +172 -0
- package/dist/platform/organization-sharing.js +120 -0
- package/dist/platform/organizations.d.ts +205 -0
- package/dist/platform/organizations.js +180 -0
- package/dist/platform/provision-store.d.ts +88 -8
- package/dist/platform/provision-store.js +12 -0
- package/dist/platform/reports.d.ts +46 -0
- package/dist/platform/reports.js +31 -0
- package/dist/platform/server-metrics.d.ts +58 -0
- package/dist/platform/server-metrics.js +11 -0
- package/dist/pos/index.d.ts +567 -0
- package/dist/pos/index.js +205 -0
- package/dist/pricing/index.d.ts +408 -0
- package/dist/pricing/index.js +353 -0
- package/dist/pricing/vouchers.d.ts +103 -0
- package/dist/pricing/vouchers.js +69 -0
- package/dist/provenance/index.d.ts +93 -0
- package/dist/provenance/index.js +53 -0
- package/dist/reports/index.d.ts +199 -0
- package/dist/reports/index.js +154 -0
- package/dist/returns/index.d.ts +225 -0
- package/dist/returns/index.js +160 -0
- package/dist/search/index.d.ts +99 -0
- package/dist/search/index.js +62 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +52 -0
- package/dist/shipping/parcel-count-suggestion.js +73 -0
- package/dist/shipping/parcel-rule.d.ts +48 -0
- package/dist/shipping/parcel-rule.js +91 -0
- package/dist/stock/index.d.ts +166 -0
- package/dist/stock/index.js +115 -0
- package/dist/stock/transfers.d.ts +520 -0
- package/dist/stock/transfers.js +437 -0
- package/dist/stored-value/index.d.ts +154 -0
- package/dist/stored-value/index.js +130 -0
- package/dist/tenant/capabilities.d.ts +10 -0
- package/dist/tenant/entitlements.d.ts +35 -0
- package/dist/tenant/entitlements.js +15 -0
- package/dist/tenant/field-groups.d.ts +64 -0
- package/dist/tenant/field-groups.js +34 -0
- package/dist/tenant/legal-entity.d.ts +32 -0
- package/dist/tenant/legal-entity.js +41 -0
- package/dist/tenant/notifications.d.ts +148 -0
- package/dist/tenant/notifications.js +79 -0
- package/dist/tenant/retention.d.ts +10 -4
- package/dist/tenant/settings-sources.d.ts +103 -0
- package/dist/tenant/settings-sources.js +92 -0
- package/dist/types.d.ts +86 -11
- package/dist/uploads/index.d.ts +11 -7
- package/dist/users/index.d.ts +104 -0
- package/dist/users/index.js +80 -0
- 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>;
|
package/dist/api-tokens/index.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/apps/index.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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;
|
package/dist/apps/index.js
CHANGED
|
@@ -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;
|
package/dist/audit/index.d.ts
CHANGED
|
@@ -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>>;
|
package/dist/auth/queries.d.ts
CHANGED
|
@@ -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
|
-
|
|
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";
|
package/dist/auth/queries.js
CHANGED
|
@@ -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 {
|