@sixtyninecommerce/admin 0.6.0 → 0.8.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 (39) hide show
  1. package/README.md +359 -2
  2. package/dist/apps/assertion.d.ts +30 -3
  3. package/dist/apps/assertion.js +40 -3
  4. package/dist/apps/config.d.ts +39 -0
  5. package/dist/apps/config.js +22 -0
  6. package/dist/apps/credential.d.ts +5 -0
  7. package/dist/apps/credential.js +55 -4
  8. package/dist/apps/index.d.ts +11 -0
  9. package/dist/client.d.ts +54 -1
  10. package/dist/client.js +34 -1
  11. package/dist/custom-objects/index.d.ts +66 -0
  12. package/dist/custom-objects/index.js +48 -0
  13. package/dist/customers/index.d.ts +225 -0
  14. package/dist/customers/index.js +279 -0
  15. package/dist/documents/index.d.ts +432 -0
  16. package/dist/documents/index.js +289 -0
  17. package/dist/index.d.ts +34 -2
  18. package/dist/index.js +16 -1
  19. package/dist/jobs/index.d.ts +143 -0
  20. package/dist/jobs/index.js +132 -0
  21. package/dist/metafields/index.d.ts +291 -0
  22. package/dist/metafields/index.js +141 -0
  23. package/dist/orders/index.d.ts +106 -0
  24. package/dist/orders/index.js +115 -0
  25. package/dist/orders/surcharges.d.ts +121 -0
  26. package/dist/orders/surcharges.js +110 -0
  27. package/dist/payments/index.d.ts +244 -0
  28. package/dist/payments/index.js +103 -0
  29. package/dist/payments/providers.d.ts +281 -0
  30. package/dist/payments/providers.js +205 -0
  31. package/dist/platform/provision-store.d.ts +9 -26
  32. package/dist/platform/provision-store.js +8 -66
  33. package/dist/tax/index.d.ts +128 -0
  34. package/dist/tax/index.js +101 -0
  35. package/dist/tenant/capabilities.d.ts +15 -0
  36. package/dist/types.d.ts +20 -3
  37. package/dist/uploads/index.d.ts +146 -0
  38. package/dist/uploads/index.js +72 -0
  39. package/package.json +3 -3
@@ -0,0 +1,289 @@
1
+ import { createPage, ValidationError } from '@sixtyninecommerce/core';
2
+ const API_DOCUMENT_TYPE = {
3
+ invoice: 'INVOICE',
4
+ 'credit-note': 'CREDIT_NOTE',
5
+ 'packing-slip': 'PACKING_SLIP',
6
+ };
7
+ /**
8
+ * Most blocks one document may carry. A longer list refuses the whole document
9
+ * (`DOCUMENT_EXTENSION_INVALID`) rather than issuing a numbered one that
10
+ * dropped the overflow — a document is a page, not a feed.
11
+ */
12
+ export const MAX_DOCUMENT_EXTENSIONS = 10;
13
+ const INVOICE_FIELDS = /* GraphQL */ `
14
+ id
15
+ invoiceNumber
16
+ invoicedOn
17
+ orderId
18
+ languageCode
19
+ storageKey
20
+ totalWithTax
21
+ currencyCode
22
+ note
23
+ voidedOn
24
+ kind
25
+ createdAt
26
+ `;
27
+ const CREDIT_NOTE_FIELDS = /* GraphQL */ `
28
+ id
29
+ creditNoteNumber
30
+ issuedOn
31
+ totalWithTax
32
+ currencyCode
33
+ reason
34
+ invoiceId
35
+ kind
36
+ storageKey
37
+ createdAt
38
+ `;
39
+ const PACKING_SLIP_FIELDS = /* GraphQL */ `
40
+ id
41
+ packingSlipNumber
42
+ fulfillmentId
43
+ languageCode
44
+ storageKey
45
+ createdAt
46
+ `;
47
+ const DOCUMENT_EXTENSION_FIELDS = /* GraphQL */ `
48
+ key
49
+ documentTypes
50
+ schemaVersion
51
+ jsonSchema
52
+ templateFragment
53
+ createdAt
54
+ updatedAt
55
+ `;
56
+ export const GENERATE_INVOICE_MUTATION = /* GraphQL */ `
57
+ mutation GenerateInvoice(
58
+ $orderId: ID!
59
+ $invoicedOn: String!
60
+ $languageCode: String
61
+ $note: String
62
+ $extensions: [DocumentExtensionInput!]
63
+ ) {
64
+ generateInvoice(
65
+ orderId: $orderId
66
+ invoicedOn: $invoicedOn
67
+ languageCode: $languageCode
68
+ note: $note
69
+ extensions: $extensions
70
+ ) { ${INVOICE_FIELDS} }
71
+ }
72
+ `;
73
+ export const GENERATE_PACKING_SLIP_MUTATION = /* GraphQL */ `
74
+ mutation GeneratePackingSlip(
75
+ $fulfillmentId: ID!
76
+ $languageCode: String
77
+ $extensions: [DocumentExtensionInput!]
78
+ ) {
79
+ generatePackingSlip(
80
+ fulfillmentId: $fulfillmentId
81
+ languageCode: $languageCode
82
+ extensions: $extensions
83
+ ) { ${PACKING_SLIP_FIELDS} }
84
+ }
85
+ `;
86
+ export const DOCUMENT_DATA_QUERY = /* GraphQL */ `
87
+ query DocumentData($documentId: ID!, $type: DocumentType!) {
88
+ documentData(documentId: $documentId, type: $type)
89
+ }
90
+ `;
91
+ export const ORDER_INVOICES_QUERY = /* GraphQL */ `
92
+ query OrderInvoices($orderId: ID!) {
93
+ orderInvoices(orderId: $orderId) { ${INVOICE_FIELDS} }
94
+ }
95
+ `;
96
+ export const ORDER_CREDIT_NOTES_QUERY = /* GraphQL */ `
97
+ query OrderCreditNotes($orderId: ID!) {
98
+ orderCreditNotes(orderId: $orderId) { ${CREDIT_NOTE_FIELDS} }
99
+ }
100
+ `;
101
+ export const PREVIEW_INVOICE_MUTATION = /* GraphQL */ `
102
+ mutation PreviewInvoice($orderId: ID!, $invoicedOn: String!, $languageCode: String, $note: String) {
103
+ previewInvoice(orderId: $orderId, invoicedOn: $invoicedOn, languageCode: $languageCode, note: $note)
104
+ }
105
+ `;
106
+ export const SEND_INVOICE_EMAIL_MUTATION = /* GraphQL */ `
107
+ mutation SendInvoiceEmail($input: SendInvoiceEmailInput!) {
108
+ sendInvoiceEmail(input: $input)
109
+ }
110
+ `;
111
+ export const ISSUE_PARTIAL_CREDIT_NOTE_MUTATION = /* GraphQL */ `
112
+ mutation IssuePartialCreditNote($input: IssuePartialCreditNoteInput!) {
113
+ issuePartialCreditNote(input: $input) { ${CREDIT_NOTE_FIELDS} }
114
+ }
115
+ `;
116
+ export const ISSUE_SUPPLEMENTARY_INVOICE_MUTATION = /* GraphQL */ `
117
+ mutation IssueSupplementaryInvoice($input: IssueSupplementaryInvoiceInput!) {
118
+ issueSupplementaryInvoice(input: $input) { ${INVOICE_FIELDS} }
119
+ }
120
+ `;
121
+ export const DOCUMENT_EXTENSIONS_QUERY = /* GraphQL */ `
122
+ query DocumentExtensions {
123
+ documentExtensions { ${DOCUMENT_EXTENSION_FIELDS} }
124
+ }
125
+ `;
126
+ export const REGISTER_DOCUMENT_EXTENSION_MUTATION = /* GraphQL */ `
127
+ mutation RegisterDocumentExtension($input: RegisterDocumentExtensionInput!) {
128
+ registerDocumentExtension(input: $input) { ${DOCUMENT_EXTENSION_FIELDS} }
129
+ }
130
+ `;
131
+ export const DELETE_DOCUMENT_EXTENSION_MUTATION = /* GraphQL */ `
132
+ mutation DeleteDocumentExtension($key: String!) {
133
+ deleteDocumentExtension(key: $key)
134
+ }
135
+ `;
136
+ /** A complete array behind the SDK's page shape (spec §9.2). */
137
+ function wholeList(items, what) {
138
+ return createPage(items, { nextCursor: null, hasNext: false, total: items.length }, () => {
139
+ throw new Error(`${what} returns a complete list; there is no next page`);
140
+ });
141
+ }
142
+ function toApiType(type) {
143
+ const mapped = API_DOCUMENT_TYPE[type];
144
+ if (mapped === undefined) {
145
+ throw new ValidationError({
146
+ code: 'INVALID_DOCUMENT_TYPE',
147
+ message: `"${String(type)}" is not a document type. Expected one of ${Object.keys(API_DOCUMENT_TYPE).join(', ')}.`,
148
+ status: 400,
149
+ service: 'vendure',
150
+ });
151
+ }
152
+ return mapped;
153
+ }
154
+ /**
155
+ * Decode leniently: a kind this SDK does not know is passed through as the
156
+ * server spelled it rather than dropped, so a client one version behind still
157
+ * sees that the block is registered for something — which is why the decoded
158
+ * field is typed `string[]`.
159
+ */
160
+ function fromApiTypes(values) {
161
+ const byApi = new Map(Object.entries(API_DOCUMENT_TYPE).map(([type, api]) => [api, type]));
162
+ return values.map((value) => byApi.get(value) ?? value);
163
+ }
164
+ function toSchema(raw) {
165
+ return { ...raw, documentTypes: fromApiTypes(raw.documentTypes) };
166
+ }
167
+ /** Decode the base64 a PDF-returning mutation answers with. */
168
+ function decodePdf(base64) {
169
+ // Node and every modern runtime have `atob`; Buffer would tie this package
170
+ // to Node, and it ships to browsers as the dashboard's client.
171
+ const binary = atob(base64);
172
+ const bytes = new Uint8Array(binary.length);
173
+ for (let index = 0; index < binary.length; index += 1) {
174
+ bytes[index] = binary.charCodeAt(index);
175
+ }
176
+ return bytes;
177
+ }
178
+ export function createDocumentsModule(transport) {
179
+ return {
180
+ // One implementation behind the two overloads the interface declares; the
181
+ // cast is the standard TypeScript shape for that, since an implementation
182
+ // signature is never itself part of the public type.
183
+ generate: (async (subjectId, type, options = {}) => {
184
+ if (type === 'invoice') {
185
+ if (!options.invoicedOn) {
186
+ // Not defaulted to today: the document date decides which period the
187
+ // money books into, and the caller's clock is not the store's.
188
+ throw new ValidationError({
189
+ code: 'INVALID_INVOICED_ON',
190
+ message: 'generate(orderId, "invoice") requires options.invoicedOn (YYYY-MM-DD) — an invoice date is never assumed.',
191
+ status: 400,
192
+ service: 'vendure',
193
+ });
194
+ }
195
+ const data = await transport.graphql({
196
+ api: 'admin',
197
+ query: GENERATE_INVOICE_MUTATION,
198
+ variables: {
199
+ orderId: subjectId,
200
+ invoicedOn: options.invoicedOn,
201
+ languageCode: options.languageCode ?? null,
202
+ note: options.note ?? null,
203
+ extensions: options.extensions ?? null,
204
+ },
205
+ }, options);
206
+ return data.generateInvoice;
207
+ }
208
+ if (options.invoicedOn !== undefined || options.note !== undefined) {
209
+ throw new ValidationError({
210
+ code: 'INVALID_DOCUMENT_OPTIONS',
211
+ message: 'A packing slip has no invoice date and no note; pass only languageCode and extensions.',
212
+ status: 400,
213
+ service: 'vendure',
214
+ });
215
+ }
216
+ const data = await transport.graphql({
217
+ api: 'admin',
218
+ query: GENERATE_PACKING_SLIP_MUTATION,
219
+ variables: {
220
+ fulfillmentId: subjectId,
221
+ languageCode: options.languageCode ?? null,
222
+ extensions: options.extensions ?? null,
223
+ },
224
+ }, options);
225
+ return data.generatePackingSlip;
226
+ }),
227
+ async data(documentId, type, options) {
228
+ const data = await transport.graphql({
229
+ api: 'admin',
230
+ query: DOCUMENT_DATA_QUERY,
231
+ variables: { documentId, type: toApiType(type) },
232
+ }, options);
233
+ return data.documentData;
234
+ },
235
+ async invoices(orderId, options) {
236
+ const data = await transport.graphql({ api: 'admin', query: ORDER_INVOICES_QUERY, variables: { orderId } }, options);
237
+ return wholeList(data.orderInvoices, 'orderInvoices');
238
+ },
239
+ async creditNotes(orderId, options) {
240
+ const data = await transport.graphql({ api: 'admin', query: ORDER_CREDIT_NOTES_QUERY, variables: { orderId } }, options);
241
+ return wholeList(data.orderCreditNotes, 'orderCreditNotes');
242
+ },
243
+ async previewInvoice(orderId, options) {
244
+ const data = await transport.graphql({
245
+ api: 'admin',
246
+ query: PREVIEW_INVOICE_MUTATION,
247
+ variables: {
248
+ orderId,
249
+ invoicedOn: options.invoicedOn,
250
+ languageCode: options.languageCode ?? null,
251
+ note: options.note ?? null,
252
+ },
253
+ }, options);
254
+ return decodePdf(data.previewInvoice);
255
+ },
256
+ async sendInvoiceEmail(input, options) {
257
+ const data = await transport.graphql({ api: 'admin', query: SEND_INVOICE_EMAIL_MUTATION, variables: { input } }, options);
258
+ return data.sendInvoiceEmail;
259
+ },
260
+ async issuePartialCreditNote(input, options) {
261
+ const data = await transport.graphql({ api: 'admin', query: ISSUE_PARTIAL_CREDIT_NOTE_MUTATION, variables: { input } }, options);
262
+ return data.issuePartialCreditNote;
263
+ },
264
+ async issueSupplementaryInvoice(input, options) {
265
+ const data = await transport.graphql({ api: 'admin', query: ISSUE_SUPPLEMENTARY_INVOICE_MUTATION, variables: { input } }, options);
266
+ return data.issueSupplementaryInvoice;
267
+ },
268
+ extensions: {
269
+ async list(options) {
270
+ const data = await transport.graphql({ api: 'admin', query: DOCUMENT_EXTENSIONS_QUERY }, options);
271
+ return wholeList(data.documentExtensions.map(toSchema), 'documentExtensions');
272
+ },
273
+ async register(input, options) {
274
+ const data = await transport.graphql({
275
+ api: 'admin',
276
+ query: REGISTER_DOCUMENT_EXTENSION_MUTATION,
277
+ variables: {
278
+ input: { ...input, documentTypes: input.documentTypes.map(toApiType) },
279
+ },
280
+ }, options);
281
+ return toSchema(data.registerDocumentExtension);
282
+ },
283
+ async delete(key, options) {
284
+ const data = await transport.graphql({ api: 'admin', query: DELETE_DOCUMENT_EXTENSION_MUTATION, variables: { key } }, options);
285
+ return data.deleteDocumentExtension;
286
+ },
287
+ },
288
+ };
289
+ }
package/dist/index.d.ts CHANGED
@@ -18,31 +18,63 @@
18
18
  * are defined — without this they can only pass inline literals.
19
19
  */
20
20
  export type { DeprecationNotice, Page, PageMeta, RateLimitInfo, RetryPolicy, } from '@sixtyninecommerce/core';
21
+ /**
22
+ * The canonical app-manifest grammar, re-exported for the same reason the error
23
+ * hierarchy is: it is defined in `@sixtyninecommerce/core`, which is a
24
+ * transitive dependency pnpm does not resolve for a consumer. A surface that
25
+ * accepts a pasted manifest — the dashboard's registry page — needs the schema
26
+ * to say which keys exist at all without hand-copying a second, looser list.
27
+ *
28
+ * The registry is deliberately STRICTER than this schema (it refuses a padded
29
+ * string, a duplicate entry, a namespace naming another app), so validating
30
+ * against it says "worth sending", never "certain to be accepted".
31
+ */
21
32
  /**
22
33
  * The error hierarchy this package throws, re-exported so a consumer can catch
23
34
  * it without adding a direct dependency on `@sixtyninecommerce/core` — which,
24
35
  * being transitive, pnpm would not resolve for them anyway.
25
36
  */
26
- export { AuthenticationError, BusinessRuleError, ConflictError, type ErrorDetail, NotFoundError, PermissionDeniedError, PlatformError, type ProblemDetails, RateLimitedError, TransportError, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
37
+ export { APP_MANIFEST_SCHEMA, APP_MANIFEST_SCHEMA_SOURCE, AuthenticationError, BusinessRuleError, ConflictError, type ErrorDetail, NotFoundError, PermissionDeniedError, PlatformError, type ProblemDetails, RateLimitedError, TransportError, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
27
38
  export type { ApiTokensModule, CreatedApiToken } from './api-tokens/index.js';
28
39
  export type { AppCryptoKey, AppPrivateKey } from './apps/assertion.js';
40
+ export type { AppInstallationConfig, AppInstallationConfigModule } from './apps/config.js';
29
41
  export type { AppCredential, AppCredentialConfig } from './apps/credential.js';
30
42
  export type { App, AppInstallation, ApproveConsentInput, AppsModule, InstallAppInput, } from './apps/index.js';
31
43
  export type { AuditCall, AuditCallFilter, AuditCorrelation, AuditEvent, AuditEventFilter, AuditModule, } from './audit/index.js';
32
44
  export type { AuthModule } from './auth/index.js';
33
45
  export { type AdminClient, type AdminClientConfig, type AppAuthConfig, createAdminClient, type SessionConfig, } from './client.js';
34
46
  export type { Form, FormField, FormFieldInput, FormFieldTranslation, FormFieldTranslationInput, FormInput, FormsModule, FormTranslation, FormTranslationInput, } from './content/forms.js';
47
+ export type { CustomObject, CustomObjectsModule } from './custom-objects/index.js';
48
+ export type { CreateCustomerInput, CreatedCustomer, Customer, CustomerAddress, CustomerAddressesModule, CustomerErasureRecord, CustomerGroupRef, CustomerGroupsModule, CustomerInvitation, CustomerInviteMode, CustomersModule, EraseCustomerOptions, UpdateCustomerInput, } from './customers/index.js';
49
+ export type { CreditNote, DocumentData, DocumentDataIssuer, DocumentDataLine, DocumentDataNumbering, DocumentDataParty, DocumentDataTaxRow, DocumentDataTotals, DocumentExtensionInput, DocumentExtensionSchema, DocumentExtensionsModule, DocumentLineInput, DocumentsModule, DocumentType, GenerateDocumentOptions, Invoice, IssuePartialCreditNoteInput, IssueSupplementaryInvoiceInput, PackingSlip, RegisterDocumentExtensionInput, SendInvoiceEmailInput, } from './documents/index.js';
50
+ export { MAX_DOCUMENT_EXTENSIONS } from './documents/index.js';
35
51
  export { AdminProjectionError } from './errors.js';
36
52
  export type { CreateHeadInput, Head, HeadsModule, UpdateHeadInput, } from './heads/index.js';
53
+ /**
54
+ * `JobHandle` and `WaitOptions` are exported from here AND from
55
+ * `./platform/provision-store.js`, which re-exports them: they used to live
56
+ * there, and a consumer's import must not break because the platform grew a
57
+ * generic jobs resource around them.
58
+ */
59
+ export type { Job, JobFilter, JobHandle, JobStatus, JobsModule, WaitOptions, } from './jobs/index.js';
60
+ export { TERMINAL_JOB_STATUSES } from './jobs/index.js';
37
61
  export type { LocationProfile, LocationStockLocation, LocationsModule, OpeningHours, OpeningHoursDay, OpeningHoursInterval, StockLocationAddress, StockLocationAddressInput, UpsertLocationProfileInput, } from './locations/index.js';
38
62
  export { OPENING_HOURS_DAYS } from './locations/index.js';
39
63
  export type { ApiTokenTemplate, JsonSchema, LabelMap, MetaCatalog, MetaCatalogLabels, MetaCatalogPermissions, MetaCatalogPlugins, MetaCatalogTransitions, MetaModule, PermissionGroup, PluginCatalogEntry, RoleTemplate, StatusMetaMap, StatusTone, TransitionMap, } from './meta/index.js';
64
+ export type { Metafield, MetafieldAttachment, MetafieldDefinition, MetafieldDefinitionsModule, MetafieldMoney, MetafieldOwnerType, MetafieldReference, MetafieldScalarType, MetafieldsModule, MetafieldType, MetafieldVisibility, SetMetafieldInput, UpsertMetafieldDefinitionInput, } from './metafields/index.js';
65
+ export { METAFIELD_OWNER_TYPES, METAFIELD_SCALAR_TYPES } from './metafields/index.js';
66
+ export type { OrderSettlement, OrdersModule } from './orders/index.js';
67
+ export type { SurchargeCall, SurchargedOrder, SurchargeInput, SurchargesModule, } from './orders/surcharges.js';
68
+ export type { IssuedRefund, OrderPaymentPosition, OrderRefundPosition, PaidOrder, PaymentsModule, ProviderConnection, ProviderCredentialPath, ProviderOnboardingStart, ProviderReadiness, ProvidersModule, ProviderTerminal, ProviderTerminalsModule, ProvisionTerminalInput, ReadinessOptions, RecordPaymentInput, RefundablePayment, RefundPaymentInput, } from './payments/index.js';
40
69
  export type { AppKey, PlatformAppsModule, RegisterAppResult, RegisteredApp, } from './platform/apps.js';
41
70
  export { requireHumanSession } from './platform/guard.js';
42
- export type { JobHandle, PlatformModule, ProvisionProfile, ProvisionRun, ProvisionRunStatus, ProvisionStep, ProvisionStepStatus, ProvisionStoreInput, ProvisionStoreResult, WaitOptions, } from './platform/provision-store.js';
71
+ export type { PlatformModule, ProvisionProfile, ProvisionRun, ProvisionRunStatus, ProvisionStep, ProvisionStepStatus, ProvisionStoreInput, ProvisionStoreResult, } from './platform/provision-store.js';
43
72
  export { PROVISION_PROFILES } from './platform/provision-store.js';
44
73
  export type { Redirect, RedirectCreateInput, RedirectsModule, RedirectUpdateInput, } from './seo/redirects.js';
74
+ export type { TaxClass, TaxClassesModule, TaxClassMapping, TaxClassTaxCategory, TaxMappingsModule, TaxModule, UpsertTaxClassInput, } from './tax/index.js';
75
+ export { TAX_CLASS_FALLBACK_COUNTRY } from './tax/index.js';
45
76
  export type { CapabilitiesModule, Capability, CapabilityManifest, } from './tenant/capabilities.js';
46
77
  export type { ChannelsModule } from './tenant/channels.js';
47
78
  export type { ChannelRetention, RetentionHold, RetentionModule, SetRetentionInput, } from './tenant/retention.js';
48
79
  export type { ApiToken, Call, ChannelInfo, CreateApiTokenInput, ListOptions, Me, TenantContext, TenantScope, UpdateApiTokenInput, WhoAmI, } from './types.js';
80
+ export type { ClaimAsAssetInput, StagedUpload, StagedUploadTicket, StageUploadInput, UploadBody, UploadPurpose, UploadStatus, UploadsModule, } from './uploads/index.js';
package/dist/index.js CHANGED
@@ -11,14 +11,29 @@
11
11
  * dashboard is one — but constructing a client with an API token there throws
12
12
  * `CREDENTIAL_IN_BROWSER` (spec §18 amendment 3).
13
13
  */
14
+ /**
15
+ * The canonical app-manifest grammar, re-exported for the same reason the error
16
+ * hierarchy is: it is defined in `@sixtyninecommerce/core`, which is a
17
+ * transitive dependency pnpm does not resolve for a consumer. A surface that
18
+ * accepts a pasted manifest — the dashboard's registry page — needs the schema
19
+ * to say which keys exist at all without hand-copying a second, looser list.
20
+ *
21
+ * The registry is deliberately STRICTER than this schema (it refuses a padded
22
+ * string, a duplicate entry, a namespace naming another app), so validating
23
+ * against it says "worth sending", never "certain to be accepted".
24
+ */
14
25
  /**
15
26
  * The error hierarchy this package throws, re-exported so a consumer can catch
16
27
  * it without adding a direct dependency on `@sixtyninecommerce/core` — which,
17
28
  * being transitive, pnpm would not resolve for them anyway.
18
29
  */
19
- export { AuthenticationError, BusinessRuleError, ConflictError, NotFoundError, PermissionDeniedError, PlatformError, RateLimitedError, TransportError, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
30
+ export { APP_MANIFEST_SCHEMA, APP_MANIFEST_SCHEMA_SOURCE, AuthenticationError, BusinessRuleError, ConflictError, NotFoundError, PermissionDeniedError, PlatformError, RateLimitedError, TransportError, UpstreamError, ValidationError, } from '@sixtyninecommerce/core';
20
31
  export { createAdminClient, } from './client.js';
32
+ export { MAX_DOCUMENT_EXTENSIONS } from './documents/index.js';
21
33
  export { AdminProjectionError } from './errors.js';
34
+ export { TERMINAL_JOB_STATUSES } from './jobs/index.js';
22
35
  export { OPENING_HOURS_DAYS } from './locations/index.js';
36
+ export { METAFIELD_OWNER_TYPES, METAFIELD_SCALAR_TYPES } from './metafields/index.js';
23
37
  export { requireHumanSession } from './platform/guard.js';
24
38
  export { PROVISION_PROFILES } from './platform/provision-store.js';
39
+ export { TAX_CLASS_FALLBACK_COUNTRY } from './tax/index.js';
@@ -0,0 +1,143 @@
1
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
2
+ import type { Call, ListOptions } from '../types.js';
3
+ /**
4
+ * One long-running operation, whatever runs it (spec §6.11).
5
+ *
6
+ * Several engines run behind this — store provisioning, demo seeding, the
7
+ * platform's object-storage moves and purges, data pipelines, and the commerce
8
+ * engine's own job queue — and a caller needs none of that: one id, one status
9
+ * vocabulary, one way to wait. More engines are adopted over time, and a `kind`
10
+ * this release does not list simply has no jobs to return yet.
11
+ */
12
+ export interface Job {
13
+ id: string;
14
+ /**
15
+ * The tenant the work belongs to, or null for platform work that has none yet
16
+ * — a store provisioning run has no channel until it builds one. A tenant
17
+ * credential only ever sees its own.
18
+ */
19
+ channelId: string | null;
20
+ /**
21
+ * `<domain>.<verb>`: `store.provision`, `demo.seed`, `r2.move`, `r2.purge`,
22
+ * `pipeline.import`, `pipeline.export`, `store.rename`, `store.delete`,
23
+ * `warehouse.document`.
24
+ */
25
+ kind: string;
26
+ status: JobStatus;
27
+ /**
28
+ * What the engine can say about how far it is. Every field is optional
29
+ * because the engines genuinely differ: a provisioning run counts the steps
30
+ * it has finished, an object-storage move counts objects, and a seeding run
31
+ * only names the step it is on.
32
+ */
33
+ progress: {
34
+ done?: number;
35
+ total?: number;
36
+ message?: string;
37
+ };
38
+ /** The principal that asked for the work, when the engine recorded one. */
39
+ submittedBy: string | null;
40
+ /** ISO-8601 UTC, or null while the work is still queued. */
41
+ startedAt: string | null;
42
+ finishedAt: string | null;
43
+ /** Where the output landed, for the kinds that produce one. */
44
+ resultUrl?: string | null;
45
+ /**
46
+ * Why it failed. `code` is stable and safe to branch on — a provisioning run
47
+ * that undid everything it had built answers `PROVISION_RUN_COMPENSATED`,
48
+ * which is the difference between "retry" and "call an operator".
49
+ */
50
+ error?: {
51
+ code: string;
52
+ message: string;
53
+ } | null;
54
+ createdAt: string;
55
+ [extra: string]: unknown;
56
+ }
57
+ /** The five statuses, whatever the engine's own vocabulary is. */
58
+ export type JobStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
59
+ /** A job nothing more will happen to. */
60
+ export declare const TERMINAL_JOB_STATUSES: readonly JobStatus[];
61
+ export interface JobFilter extends ListOptions {
62
+ /** Exact kind, e.g. `store.provision`. */
63
+ kind?: string;
64
+ status?: JobStatus;
65
+ /**
66
+ * One channel's jobs. Only a platform credential has anything to narrow —
67
+ * every other caller is already confined to its own channel, and naming
68
+ * another one simply returns nothing.
69
+ */
70
+ channelId?: string;
71
+ }
72
+ export interface WaitOptions {
73
+ /** How often to poll. Default 2000 ms; anything below 250 ms is raised to it. */
74
+ intervalMs?: number;
75
+ /** Give up after this long. Default 300000 ms (5 minutes). */
76
+ timeoutMs?: number;
77
+ signal?: AbortSignal;
78
+ }
79
+ /**
80
+ * A long-running operation in flight (spec §6.11). `runId` is the receipt: it
81
+ * outlives this process, so a caller that cannot wait can hand it on and poll
82
+ * it later.
83
+ */
84
+ export interface JobHandle<T> {
85
+ runId: string;
86
+ /**
87
+ * Poll until the run reaches a terminal status, then resolve with its result
88
+ * — or throw what the failure means for the caller.
89
+ */
90
+ wait(options?: WaitOptions): Promise<T>;
91
+ }
92
+ export interface JobsModule {
93
+ /** This tenant's jobs, newest first. */
94
+ list(filter?: JobFilter): Promise<Page<Job>>;
95
+ get(id: string, options?: Call): Promise<Job>;
96
+ /**
97
+ * Wait for a job to end, and resolve with it WHATEVER the ending was —
98
+ * `succeeded`, `failed` or `cancelled`. It does not throw on a failed job:
99
+ * this resource covers engines whose failures mean different things, and the
100
+ * job carries its own `error.code` to tell them apart. It throws only
101
+ * when waiting itself could not continue: `JOB_WAIT_TIMEOUT`, an aborted
102
+ * signal, or a read that failed for a reason polling cannot fix.
103
+ */
104
+ wait(id: string, options?: WaitOptions & Call): Promise<Job>;
105
+ /**
106
+ * Ask the engine to stop.
107
+ *
108
+ * Best-effort BY KIND, and honest about it: a kind whose engine cannot stop a
109
+ * run in flight is refused with `CANCEL_NOT_SUPPORTED` (a `BusinessRuleError`)
110
+ * rather than reported as cancelled. A provisioning run never can — it either
111
+ * finishes or undoes itself — while an object-storage move stops at its next
112
+ * page.
113
+ */
114
+ cancel(id: string, options?: Call): Promise<{
115
+ id: string;
116
+ status: JobStatus;
117
+ }>;
118
+ }
119
+ export declare function createJobsModule(transport: Transport): JobsModule;
120
+ /** What a poll loop needs to know about the thing it is waiting for. */
121
+ export interface PollUntilTerminal<T> extends WaitOptions {
122
+ read(signal?: AbortSignal): Promise<T>;
123
+ isTerminal(value: T): boolean;
124
+ /**
125
+ * Build the error when waiting gives up. It receives the last value read, so
126
+ * a timeout can say what the work was doing — which is the whole reason the
127
+ * loop keeps it.
128
+ */
129
+ onTimeout(lastSeen: T | undefined): Error;
130
+ }
131
+ /**
132
+ * The polling loop every `wait()` in this SDK shares.
133
+ *
134
+ * Promoted out of `platform/provision-store.ts`, where it was written for one
135
+ * resource: the backoff, the abort handling and — the part worth keeping — the
136
+ * distinction between a read that failed because the SERVER hiccuped and one
137
+ * that failed because the answer is no. The first is retried a few times; the
138
+ * second is thrown at once, because polling a 404 forever helps nobody.
139
+ */
140
+ export declare function pollUntilTerminal<T>(options: PollUntilTerminal<T>): Promise<T>;
141
+ /** A read that failed for a reason polling again cannot fix. */
142
+ export declare function isTerminalPollError(error: unknown): boolean;
143
+ export declare function delay(ms: number, signal?: AbortSignal): Promise<void>;
@@ -0,0 +1,132 @@
1
+ import { createPage, PLATFORM_PASSTHROUGH_KEYS, readRestPage, TransportError, } from '@sixtyninecommerce/core';
2
+ /** A job nothing more will happen to. */
3
+ export const TERMINAL_JOB_STATUSES = ['succeeded', 'failed', 'cancelled'];
4
+ const DEFAULT_INTERVAL_MS = 2_000;
5
+ const MIN_INTERVAL_MS = 250;
6
+ const DEFAULT_TIMEOUT_MS = 300_000;
7
+ /**
8
+ * Consecutive failed READS tolerated while waiting. A poll is a read of work
9
+ * that is still going: one 503 from a rolling restart must not report a failure
10
+ * for a run that is fine. Reset by any successful read, so only a sustained
11
+ * outage gives up — and a refusal that is ABOUT the job (not found, no
12
+ * permission) is not retried at all.
13
+ */
14
+ const MAX_CONSECUTIVE_POLL_FAILURES = 3;
15
+ /**
16
+ * `progress` is an engine's OWN bag — a pipeline counts per entity type, a
17
+ * seeding run names a step — so its keys are passed through uncased and read
18
+ * exactly as the engine wrote them.
19
+ *
20
+ * `error` is NOT in that set and is not meant to be: it is a fixed
21
+ * `{ code, message }` DTO, and `error` is also the legacy envelope member, so
22
+ * preserving it platform-wide would be wrong. An engine that ever wants keyed
23
+ * failure detail puts it under `progress` or gets its own passthrough key.
24
+ */
25
+ const JOB_PASSTHROUGH = PLATFORM_PASSTHROUGH_KEYS;
26
+ export function createJobsModule(transport) {
27
+ async function list(filter) {
28
+ const body = await transport.rest('plugins', {
29
+ method: 'GET',
30
+ path: '/jobs',
31
+ query: {
32
+ after: filter?.after,
33
+ limit: filter?.limit,
34
+ kind: filter?.kind,
35
+ status: filter?.status,
36
+ channel_id: filter?.channelId,
37
+ },
38
+ }, { ...filter, preserveKeys: JOB_PASSTHROUGH });
39
+ const { items, meta } = readRestPage(body);
40
+ return createPage(items, meta, (cursor) => list({ ...filter, after: cursor }));
41
+ }
42
+ async function get(id, options) {
43
+ return transport.rest('plugins', { method: 'GET', path: `/jobs/${encodeURIComponent(id)}` }, { ...options, preserveKeys: JOB_PASSTHROUGH });
44
+ }
45
+ return {
46
+ list,
47
+ get,
48
+ async wait(id, options) {
49
+ return pollUntilTerminal({
50
+ read: (signal) => get(id, { ...options, ...(signal ? { signal } : {}) }),
51
+ isTerminal: (job) => TERMINAL_JOB_STATUSES.includes(job.status),
52
+ onTimeout: (seen) => jobWaitTimeout(id, seen),
53
+ ...options,
54
+ });
55
+ },
56
+ async cancel(id, options) {
57
+ return transport.rest('plugins', { method: 'POST', path: `/jobs/${encodeURIComponent(id)}/cancel` }, options);
58
+ },
59
+ };
60
+ }
61
+ /**
62
+ * The polling loop every `wait()` in this SDK shares.
63
+ *
64
+ * Promoted out of `platform/provision-store.ts`, where it was written for one
65
+ * resource: the backoff, the abort handling and — the part worth keeping — the
66
+ * distinction between a read that failed because the SERVER hiccuped and one
67
+ * that failed because the answer is no. The first is retried a few times; the
68
+ * second is thrown at once, because polling a 404 forever helps nobody.
69
+ */
70
+ export async function pollUntilTerminal(options) {
71
+ const intervalMs = Math.max(options.intervalMs ?? DEFAULT_INTERVAL_MS, MIN_INTERVAL_MS);
72
+ const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
73
+ let consecutiveFailures = 0;
74
+ let lastSeen;
75
+ for (;;) {
76
+ let value;
77
+ try {
78
+ value = await options.read(options.signal);
79
+ consecutiveFailures = 0;
80
+ }
81
+ catch (error) {
82
+ if (options.signal?.aborted === true)
83
+ throw error;
84
+ consecutiveFailures += 1;
85
+ if (isTerminalPollError(error) || consecutiveFailures > MAX_CONSECUTIVE_POLL_FAILURES) {
86
+ throw error;
87
+ }
88
+ if (Date.now() >= deadline)
89
+ throw options.onTimeout(lastSeen);
90
+ await delay(intervalMs, options.signal);
91
+ continue;
92
+ }
93
+ lastSeen = value;
94
+ if (options.isTerminal(value))
95
+ return value;
96
+ if (Date.now() >= deadline)
97
+ throw options.onTimeout(value);
98
+ await delay(intervalMs, options.signal);
99
+ }
100
+ }
101
+ /** A read that failed for a reason polling again cannot fix. */
102
+ export function isTerminalPollError(error) {
103
+ const status = error?.status;
104
+ return typeof status === 'number' && status >= 400 && status < 500 && status !== 429;
105
+ }
106
+ function jobWaitTimeout(id, job) {
107
+ const at = job ? `while it was ${job.status}` : 'without reading it successfully';
108
+ return new TransportError({
109
+ code: 'JOB_WAIT_TIMEOUT',
110
+ message: `Stopped waiting for job ${id} ${at}. The job may still be going — poll jobs.get("${id}") to follow it.`,
111
+ status: 0,
112
+ service: 'plugins',
113
+ retryable: true,
114
+ });
115
+ }
116
+ export function delay(ms, signal) {
117
+ return new Promise((resolve, reject) => {
118
+ if (signal?.aborted) {
119
+ reject(signal.reason instanceof Error ? signal.reason : new Error('Aborted'));
120
+ return;
121
+ }
122
+ const timer = setTimeout(() => {
123
+ signal?.removeEventListener('abort', onAbort);
124
+ resolve();
125
+ }, ms);
126
+ function onAbort() {
127
+ clearTimeout(timer);
128
+ reject(signal?.reason instanceof Error ? signal.reason : new Error('Aborted'));
129
+ }
130
+ signal?.addEventListener('abort', onAbort, { once: true });
131
+ });
132
+ }