@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.
- package/README.md +359 -2
- package/dist/apps/assertion.d.ts +30 -3
- package/dist/apps/assertion.js +40 -3
- package/dist/apps/config.d.ts +39 -0
- package/dist/apps/config.js +22 -0
- package/dist/apps/credential.d.ts +5 -0
- package/dist/apps/credential.js +55 -4
- package/dist/apps/index.d.ts +11 -0
- package/dist/client.d.ts +54 -1
- package/dist/client.js +34 -1
- package/dist/custom-objects/index.d.ts +66 -0
- package/dist/custom-objects/index.js +48 -0
- package/dist/customers/index.d.ts +225 -0
- package/dist/customers/index.js +279 -0
- package/dist/documents/index.d.ts +432 -0
- package/dist/documents/index.js +289 -0
- package/dist/index.d.ts +34 -2
- package/dist/index.js +16 -1
- package/dist/jobs/index.d.ts +143 -0
- package/dist/jobs/index.js +132 -0
- package/dist/metafields/index.d.ts +291 -0
- package/dist/metafields/index.js +141 -0
- package/dist/orders/index.d.ts +106 -0
- package/dist/orders/index.js +115 -0
- package/dist/orders/surcharges.d.ts +121 -0
- package/dist/orders/surcharges.js +110 -0
- package/dist/payments/index.d.ts +244 -0
- package/dist/payments/index.js +103 -0
- package/dist/payments/providers.d.ts +281 -0
- package/dist/payments/providers.js +205 -0
- package/dist/platform/provision-store.d.ts +9 -26
- package/dist/platform/provision-store.js +8 -66
- package/dist/tax/index.d.ts +128 -0
- package/dist/tax/index.js +101 -0
- package/dist/tenant/capabilities.d.ts +15 -0
- package/dist/types.d.ts +20 -3
- package/dist/uploads/index.d.ts +146 -0
- package/dist/uploads/index.js +72 -0
- 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 {
|
|
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
|
+
}
|