@aglyn/plugins-commerce 1.0.0-beta.288 → 1.0.0-beta.290
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 +2 -2
- package/package.json +13 -13
- package/src/lib/components/commerce-console-page.js +29 -2
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/commerce-analytics-card.component.js +4 -2
- package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -1
- package/src/lib/components/console/commerce-pdf-download.d.ts +30 -0
- package/src/lib/components/console/commerce-pdf-download.js +53 -0
- package/src/lib/components/console/commerce-pdf-download.js.map +1 -0
- package/src/lib/components/console/host-orders-card.component.js +5 -1
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/host-quotes-card.component.d.ts +34 -0
- package/src/lib/components/console/host-quotes-card.component.js +263 -0
- package/src/lib/components/console/host-quotes-card.component.js.map +1 -0
- package/src/lib/components/console/order-invoice.component.d.ts +6 -4
- package/src/lib/components/console/order-invoice.component.js +67 -11
- package/src/lib/components/console/order-invoice.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +33 -28
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
- package/src/lib/components/console/quote-editor-dialog.component.d.ts +45 -0
- package/src/lib/components/console/quote-editor-dialog.component.js +1317 -0
- package/src/lib/components/console/quote-editor-dialog.component.js.map +1 -0
- package/src/lib/components/console/quote-pay-link.component.d.ts +38 -0
- package/src/lib/components/console/quote-pay-link.component.js +213 -0
- package/src/lib/components/console/quote-pay-link.component.js.map +1 -0
- package/src/lib/components/console/quote-proofs.component.d.ts +37 -0
- package/src/lib/components/console/quote-proofs.component.js +626 -0
- package/src/lib/components/console/quote-proofs.component.js.map +1 -0
- package/src/lib/components/console/quote-settings-card.component.d.ts +29 -0
- package/src/lib/components/console/quote-settings-card.component.js +121 -0
- package/src/lib/components/console/quote-settings-card.component.js.map +1 -0
- package/src/lib/components/console/tax-settings-card.component.js +4 -1
- package/src/lib/components/console/tax-settings-card.component.js.map +1 -1
- package/src/lib/components/quote-page-body.d.ts +33 -0
- package/src/lib/components/quote-page-body.js +724 -0
- package/src/lib/components/quote-page-body.js.map +1 -0
- package/src/lib/components/quote-page.d.ts +29 -0
- package/src/lib/components/quote-page.js +96 -0
- package/src/lib/components/quote-page.js.map +1 -0
- package/src/lib/components/quote-proof-panel.d.ts +42 -0
- package/src/lib/components/quote-proof-panel.js +436 -0
- package/src/lib/components/quote-proof-panel.js.map +1 -0
- package/src/lib/components/storefront-payment-element.d.ts +7 -1
- package/src/lib/components/storefront-payment-element.js +8 -3
- package/src/lib/components/storefront-payment-element.js.map +1 -1
- package/src/lib/constants/orders-list-query.js +6 -1
- package/src/lib/constants/orders-list-query.js.map +1 -1
- package/src/lib/constants/quote-page.d.ts +30 -0
- package/src/lib/constants/quote-page.js +35 -0
- package/src/lib/constants/quote-page.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +25 -2
- package/src/lib/model/commerce-orders.js +10 -1
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-quote-pay.d.ts +256 -0
- package/src/lib/model/commerce-quote-pay.js +426 -0
- package/src/lib/model/commerce-quote-pay.js.map +1 -0
- package/src/lib/model/commerce-quote-proofs.d.ts +159 -0
- package/src/lib/model/commerce-quote-proofs.js +348 -0
- package/src/lib/model/commerce-quote-proofs.js.map +1 -0
- package/src/lib/model/commerce-quotes.d.ts +335 -0
- package/src/lib/model/commerce-quotes.js +522 -0
- package/src/lib/model/commerce-quotes.js.map +1 -0
- package/src/lib/model/index.d.ts +3 -0
- package/src/lib/model/index.js +3 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/plugin-permissions.d.ts +1 -1
- package/src/lib/model/plugin-permissions.js +20 -0
- package/src/lib/model/plugin-permissions.js.map +1 -1
- package/src/lib/server/api-v1/orders-and-products.js +6 -2
- package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
- package/src/lib/server/billing-webhook.js +127 -52
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/checkout-session-expiry.d.ts +36 -0
- package/src/lib/server/checkout-session-expiry.js +63 -0
- package/src/lib/server/checkout-session-expiry.js.map +1 -0
- package/src/lib/server/commerce-pdf.d.ts +100 -0
- package/src/lib/server/commerce-pdf.js +283 -0
- package/src/lib/server/commerce-pdf.js.map +1 -0
- package/src/lib/server/draft-order.js +16 -8
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/order-print.d.ts +6 -0
- package/src/lib/server/order-print.js +22 -5
- package/src/lib/server/order-print.js.map +1 -1
- package/src/lib/server/pdf-response.d.ts +31 -0
- package/src/lib/server/pdf-response.js +30 -0
- package/src/lib/server/pdf-response.js.map +1 -0
- package/src/lib/server/person-eraser.d.ts +7 -0
- package/src/lib/server/person-eraser.js +24 -2
- package/src/lib/server/person-eraser.js.map +1 -1
- package/src/lib/server/pos-reader-splash.d.ts +2 -18
- package/src/lib/server/pos-reader-splash.js +8 -93
- package/src/lib/server/pos-reader-splash.js.map +1 -1
- package/src/lib/server/pos-return.js +3 -1
- package/src/lib/server/pos-return.js.map +1 -1
- package/src/lib/server/quote-link-token.d.ts +39 -0
- package/src/lib/server/quote-link-token.js +97 -0
- package/src/lib/server/quote-link-token.js.map +1 -0
- package/src/lib/server/quote-pay.d.ts +110 -0
- package/src/lib/server/quote-pay.js +606 -0
- package/src/lib/server/quote-pay.js.map +1 -0
- package/src/lib/server/quote-proofs.d.ts +51 -0
- package/src/lib/server/quote-proofs.js +500 -0
- package/src/lib/server/quote-proofs.js.map +1 -0
- package/src/lib/server/quote-uploads.d.ts +33 -0
- package/src/lib/server/quote-uploads.js +29 -0
- package/src/lib/server/quote-uploads.js.map +1 -0
- package/src/lib/server/quote.d.ts +63 -0
- package/src/lib/server/quote.js +846 -0
- package/src/lib/server/quote.js.map +1 -0
- package/src/lib/server/related.js +2 -0
- package/src/lib/server/related.js.map +1 -1
- package/src/lib/server/site-image-source.d.ts +38 -0
- package/src/lib/server/site-image-source.js +130 -0
- package/src/lib/server/site-image-source.js.map +1 -0
- package/src/lib/server/site-page-resolver.d.ts +2 -0
- package/src/lib/server/site-page-resolver.js +75 -0
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +33 -1
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +6 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/transfer/tracking.server.js +6 -0
- package/src/lib/transfer/tracking.server.js.map +1 -1
- package/src/lib/utils/commerce-document-pdf.d.ts +77 -0
- package/src/lib/utils/commerce-document-pdf.js +608 -0
- package/src/lib/utils/commerce-document-pdf.js.map +1 -0
- package/src/lib/utils/order-invoice.d.ts +52 -3
- package/src/lib/utils/order-invoice.js +116 -64
- package/src/lib/utils/order-invoice.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/quote-proofs.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { claimAttempt, type PluginApiHandler, type PluginApiRequest } from '@aglyn/aglyn/server'\nimport { readClientIp } from '@aglyn/aglyn/app-utils/request-ip'\nimport { VISITOR_UPLOAD_HONEYPOT_FIELD, visitorUploadFileView } from '@aglyn/aglyn/app-utils/visitor-upload'\nimport { notifyHostManagers } from '@aglyn/tenant-data-admin'\nimport * as CommerceModel from '../model'\nimport { orderStatusUrl } from './order-status-token'\nimport {\n defaultQuotePayDeps,\n limitedQuoteLink,\n loadLinkedQuote,\n quoteLinkParams,\n sendQuoteLinkOutcome,\n type QuoteLinkOutcome,\n type QuotePayDeps,\n} from './quote-pay'\nimport { quoteUploadCalls, type QuoteUploadCalls } from './quote-uploads'\n\n/*==========================================\n * THE CUSTOMER'S SIDE OF A PROOF (AGL-4096), on the business's site, behind\n * the quote's signed pay link like the page itself:\n *\n * GET /api/commerce/quote-proof?hostId=&q=&t=&file=<proofId>\n * → the current revision's proof (a picture inline, anything else as a\n * download) | 404 | 410 replaced | 429\n * POST /api/commerce/quote-upload { hostId, q, t, action: 'ticket', file: { name, contentType, size } }\n * → { ticket, upload: { url, fields }, file }\n * POST /api/commerce/quote-upload { hostId, q, t, action: 'finalize', ticket }\n * → { file: { id, name, contentType, size, uploadedAtMs } }\n * POST /api/commerce/quote-respond { hostId, q, t, v, action: 'approve', name }\n * { hostId, q, t, v, action: 'changes', comment, fileIds? }\n * + Idempotency-Key → 200 { view } | 409 { error, reason } | 410 { replaced } | 404 | 400 | 429\n *\n * Every route spends a per-address rate limit on the site BEFORE the token is\n * checked, answers a wrong token exactly as a missing quote, and answers a\n * link to an older version as replaced. The uploads are the shared visitor\n * upload (`@aglyn/tenant-data-admin/server/visitor-upload`), scoped to this\n * quote's artwork; the answer names the files by id and the server reads\n * them back from that scope, so nothing else can be attached.\n *\n * `v` is the edit of the quote the page showed, as Pay sends it: an answer to\n * proofs the customer has not seen is refused and the page is shown again.\n *=========================================*/\n\n/** Proofs one address may load per site, per ten minutes: every thumbnail of a few visits. */\nexport const QUOTE_PROOF_LOADS_PER_WINDOW = 120\n/** Upload calls (tickets and finalizes) one address may make per site, per ten minutes, before the upload's own limits. */\nexport const QUOTE_UPLOAD_CALLS_PER_WINDOW = 80\n/** Answers one address may send per site, per ten minutes. */\nexport const QUOTE_RESPONSES_PER_WINDOW = 20\n\nexport interface QuoteProofDeps extends QuotePayDeps {\n uploads(): Promise<QuoteUploadCalls>\n notify(...args: Parameters<typeof notifyHostManagers>): Promise<void>\n}\n\nexport function defaultQuoteProofDeps(): QuoteProofDeps {\n return {\n ...defaultQuotePayDeps(),\n uploads: async () => quoteUploadCalls(),\n notify: (...args) => notifyHostManagers(...args),\n }\n}\n\nfunction bodyOf(req: PluginApiRequest): Record<string, unknown> {\n const raw = req.body\n if (typeof raw === 'string') {\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === 'object' ? parsed : {}\n } catch {\n return {}\n }\n }\n return raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : {}\n}\n\nfunction headerOf(req: PluginApiRequest, name: string): string {\n const headers = (req.headers ?? {}) as Record<string, unknown>\n const value = headers[name.toLowerCase()] ?? headers[name]\n return String(Array.isArray(value) ? (value[0] ?? '') : (value ?? '')).trim()\n}\n\nconst clientIp = (req: PluginApiRequest): string | null =>\n readClientIp(req.headers as never, { remoteAddress: req.socket?.remoteAddress })\n\nconst reasonRefusal = (status: number, reason: string, error: string): QuoteLinkOutcome => ({\n status,\n body: { error, reason },\n})\n\n/** What a proof route answers: a JSON outcome, or a file to stream. */\nexport type QuoteProofOutcome = QuoteLinkOutcome & { file?: { path: string; name: string; contentType: string } }\n\n/** `GET /api/commerce/quote-proof`: one of the current revision's proofs. */\nexport async function handleQuoteProof(deps: QuoteProofDeps, req: PluginApiRequest): Promise<QuoteProofOutcome> {\n if (req.method !== 'GET') return { status: 405, body: { error: 'Method not allowed' } }\n const query = (req.query ?? {}) as Record<string, unknown>\n const { hostId, quoteId, token, complete } = quoteLinkParams(query)\n const fileId = String(query['file'] ?? '').slice(0, 200)\n if (!complete || !fileId) return { status: 400, body: { error: 'This link is incomplete.' } }\n const limited = await limitedQuoteLink(deps, req, `commerce-quote-proof:${hostId}`, QUOTE_PROOF_LOADS_PER_WINDOW)\n if (limited) return limited\n const linked = await loadLinkedQuote(deps, hostId, quoteId, token)\n if ('outcome' in linked) return linked.outcome\n const proof = CommerceModel.currentQuoteProofs(linked.loaded.order.quote).find((entry) => entry.id === fileId)\n if (!proof?.path) return { status: 404, body: { error: 'That proof is no longer on this quote.' } }\n return { status: 200, body: {}, file: { path: proof.path, name: proof.name, contentType: proof.contentType } }\n}\n\n/** `POST /api/commerce/quote-upload`: a ticket for one of the customer's files, or its finalize. */\nexport async function handleQuoteUpload(deps: QuoteProofDeps, req: PluginApiRequest): Promise<QuoteLinkOutcome> {\n if (req.method !== 'POST') return { status: 405, body: { error: 'Method not allowed' } }\n const body = bodyOf(req)\n const { hostId, quoteId, token, complete } = quoteLinkParams(body)\n if (!complete) return { status: 400, body: { error: 'This link is incomplete.' } }\n // A form a person fills never fills this; a bot that did learns nothing.\n const honeypot = body[VISITOR_UPLOAD_HONEYPOT_FIELD]\n if (typeof honeypot === 'string' && honeypot.trim()) return { status: 200, body: { received: true } }\n const limited = await limitedQuoteLink(deps, req, `commerce-quote-upload:${hostId}`, QUOTE_UPLOAD_CALLS_PER_WINDOW)\n if (limited) return limited\n const linked = await loadLinkedQuote(deps, hostId, quoteId, token)\n if ('outcome' in linked) return linked.outcome\n const { order, storeName } = linked.loaded\n // Artwork rides an answer; a quote that cannot be answered takes none.\n const refused = CommerceModel.quoteRespondRefusal(order)\n if (refused) return reasonRefusal(409, refused, CommerceModel.quoteRespondRefusalMessage(refused, storeName))\n\n const scope = { hostId, purpose: CommerceModel.QUOTE_ARTWORK_PURPOSE, purposeId: quoteId }\n const caller = { ip: clientIp(req) ?? 'no-address' }\n const uploads = await deps.uploads()\n if (body['action'] === 'ticket') {\n const minted = await uploads.mint({ scope, request: body['file'], caller })\n if ('error' in minted) {\n return {\n status: minted.status,\n body: { error: minted.error, code: minted.code },\n ...(minted.retryAfterSeconds ? { headers: { 'Retry-After': String(minted.retryAfterSeconds) } } : {}),\n }\n }\n return { status: 200, body: { ticket: minted.ticket, upload: minted.upload, file: minted.file } }\n }\n if (body['action'] === 'finalize') {\n const kept = await uploads.finalize({ scope, ticket: body['ticket'], caller })\n if ('error' in kept) return { status: kept.status, body: { error: kept.error, code: kept.code } }\n return { status: 200, body: { file: visitorUploadFileView(kept.file) } }\n }\n return { status: 400, body: { error: 'Unknown action' } }\n}\n\nconst UPDATED_MESSAGE = 'This quote was updated since you opened it. Look it over again before you answer.'\n\n/** The managers' notice of an answer: who, which quote and revision, what they said, and what to do next. */\nfunction noticeOf(input: {\n hostId: string\n quoteId: string\n order: CommerceModel.HostOrder\n response: CommerceModel.QuoteCustomerResponse\n}) {\n const { hostId, quoteId, order, response } = input\n const quote = order.quote as CommerceModel.OrderQuote\n const number = CommerceModel.formatQuoteNumber(order, quoteId)\n const customer = quote.customer?.name || quote.customer?.company || quote.customer?.email || 'The customer'\n const revision = response.revision\n const files = response.files ?? []\n const approving = response.action === 'approve'\n const payNote = quote.requireProofApproval === true ? ' They can pay the quote now.' : ''\n const details = [\n { label: 'Quote', value: number },\n { label: 'Customer', value: [quote.customer?.name, quote.customer?.company, quote.customer?.email].filter(Boolean).join(', ') },\n { label: 'Revision', value: String(revision) },\n ...(approving\n ? [{ label: 'Approved as', value: response.name ?? '' }]\n : [\n { label: 'What to change', value: response.comment ?? '' },\n ...(files.length ? [{ label: 'Files sent', value: files.map((file) => file.name).join(', ') }] : []),\n ]),\n ]\n const email = String(quote.customer?.email ?? '').trim()\n return {\n payload: {\n type: 'content.order' as const,\n title: approving ? `Proof approved — ${number} on {site}` : `Changes requested — ${number} on {site}`,\n body: approving\n ? `${customer} approved the proof for revision ${revision} of ${number}.${payNote} The approval, with the name, time and address it was given from, is on the quote.`\n : `${customer} asked for changes to revision ${revision} of ${number}` +\n (files.length ? ` and sent ${files.length} file${files.length === 1 ? '' : 's'}` : '') +\n `. Read what they asked for on the quote, then start revision ${revision + 1} with new proofs.`,\n link: `/${hostId}/products/orders?quote=${encodeURIComponent(quoteId)}`,\n },\n options: {\n emailDetails: details,\n ...(/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email) ? { replyTo: email } : {}),\n },\n }\n}\n\n/** `POST /api/commerce/quote-respond`: the customer approves the proof or asks for changes. */\nexport async function handleQuoteRespond(deps: QuoteProofDeps, req: PluginApiRequest): Promise<QuoteLinkOutcome> {\n if (req.method !== 'POST') return { status: 405, body: { error: 'Method not allowed' } }\n const body = bodyOf(req)\n const { hostId, quoteId, token, complete } = quoteLinkParams(body)\n if (!complete) return { status: 400, body: { error: 'This link is incomplete.' } }\n const honeypot = body[VISITOR_UPLOAD_HONEYPOT_FIELD]\n if (typeof honeypot === 'string' && honeypot.trim()) return { status: 200, body: { received: true } }\n const idempotencyKey = headerOf(req, 'idempotency-key').slice(0, 200)\n if (!idempotencyKey) return { status: 400, body: { error: 'An answer needs an Idempotency-Key header.' } }\n const limited = await limitedQuoteLink(deps, req, `commerce-quote-respond:${hostId}`, QUOTE_RESPONSES_PER_WINDOW)\n if (limited) return limited\n const linked = await loadLinkedQuote(deps, hostId, quoteId, token)\n if ('outcome' in linked) return linked.outcome\n const { hostRef, host, order, storeName, store } = linked.loaded\n\n // Everything the customer can be refused is decided before the claim, so\n // none of it spends the attempt.\n const seen = Number(body['v'])\n const version = Number(order.quote?.updatedAtMs) || 0\n if (!Number.isFinite(seen) || seen !== version) return reasonRefusal(409, 'updated', UPDATED_MESSAGE)\n const parsed = CommerceModel.parseQuoteResponseInput(body)\n if ('error' in parsed) return { status: 400, body: { error: parsed.error } }\n const refused = CommerceModel.quoteRespondRefusal(order)\n if (refused) return reasonRefusal(409, refused, CommerceModel.quoteRespondRefusalMessage(refused, storeName))\n\n // The files are read back from this quote's artwork, by id: nothing the\n // browser says about a file is believed, and no other record's file can\n // be named.\n let files: CommerceModel.QuoteResponseFile[] = []\n if (parsed.input.action === 'changes' && parsed.input.fileIds.length) {\n const uploads = await deps.uploads()\n const scope = { hostId, purpose: CommerceModel.QUOTE_ARTWORK_PURPOSE, purposeId: quoteId }\n const read = await Promise.all(parsed.input.fileIds.map((id) => uploads.read(scope, id)))\n if (read.some((file) => !file)) {\n return { status: 400, body: { error: 'One of your files did not finish sending. Send it again.' } }\n }\n files = read.map((file) => ({\n id: file!.id,\n name: file!.name,\n contentType: file!.contentType,\n size: file!.size,\n path: file!.path,\n }))\n }\n\n const claimed = await claimAttempt(deps.firestore(), {\n kind: 'commerce-quote-respond',\n scopeId: `${hostId}:${quoteId}`,\n orgId: '',\n key: idempotencyKey,\n busyMessage: 'Your answer is already being sent',\n })\n if ('replay' in claimed) {\n return { status: claimed.replay.status, body: claimed.replay.body as Record<string, unknown> }\n }\n const claim = claimed.claim\n const now = deps.now()\n try {\n const ref = hostRef.collection('orders').doc(quoteId)\n const settled = await deps.firestore().runTransaction(\n async (transaction): Promise<{ outcome: QuoteLinkOutcome; order?: CommerceModel.HostOrder; response?: CommerceModel.QuoteCustomerResponse }> => {\n const snapshot = await transaction.get(ref)\n const current = snapshot.exists ? (snapshot.data() as CommerceModel.HostOrder) : null\n if (!current || !CommerceModel.isQuote(current) || !current.quote) {\n return { outcome: { status: 404, body: { error: 'We could not find that quote. Check the link you were sent.' } } }\n }\n if (CommerceModel.quoteLinkVersion(current.quote) !== CommerceModel.quoteLinkVersion(order.quote)) {\n return {\n outcome: {\n status: 410,\n body: { replaced: true, error: `This quote was updated, and ${storeName || 'the business'} sent you a newer link.` },\n },\n }\n }\n if ((Number(current.quote.updatedAtMs) || 0) !== version) {\n return { outcome: reasonRefusal(409, 'updated', UPDATED_MESSAGE) }\n }\n const answered = CommerceModel.applyQuoteResponse(\n current,\n { ...parsed.input, ...(files.length ? { files } : {}) },\n { atMs: now, ip: clientIp(req), userAgent: headerOf(req, 'user-agent') || null },\n )\n if ('reason' in answered) {\n return {\n outcome: reasonRefusal(409, answered.reason, CommerceModel.quoteRespondRefusalMessage(answered.reason, storeName)),\n }\n }\n transaction.set(ref, answered.order)\n return { outcome: { status: 200, body: {} }, order: answered.order, response: answered.response }\n },\n )\n if (!settled.order || !settled.response) {\n await claim.release()\n return settled.outcome\n }\n const view = CommerceModel.buildQuotePageView({\n order: settled.order,\n orderId: quoteId,\n storeName,\n currency: String(store.get('currency') ?? 'USD'),\n receiptUrl: CommerceModel.isUnpaidQuote(settled.order) ? null : orderStatusUrl(host, hostId, quoteId),\n })\n const outcome = { status: 200, body: { view } as Record<string, unknown> }\n await claim.record(200, outcome.body)\n const notice = noticeOf({ hostId, quoteId, order: settled.order, response: settled.response })\n void deps.notify(hostId, notice.payload, notice.options)\n return outcome\n } catch (error) {\n console.error('quote respond failed', error)\n await claim.release()\n return { status: 500, body: { error: 'Your answer could not be sent. Try again.' } }\n }\n}\n\n/** `GET /api/commerce/quote-proof`. */\nexport const quoteProofHandler: PluginApiHandler = async (req, res) => {\n const deps = defaultQuoteProofDeps()\n try {\n const outcome = await handleQuoteProof(deps, req)\n if (!outcome.file) return sendQuoteLinkOutcome(res, outcome)\n const uploads = await deps.uploads()\n return await uploads.send(res as never, outcome.file)\n } catch (error) {\n console.error('quote proof failed', error)\n return sendQuoteLinkOutcome(res, { status: 500, body: { error: 'This proof could not be loaded. Try again.' } })\n }\n}\n\n/** `POST /api/commerce/quote-upload`. */\nexport const quoteUploadHandler: PluginApiHandler = async (req, res) => {\n try {\n return sendQuoteLinkOutcome(res, await handleQuoteUpload(defaultQuoteProofDeps(), req))\n } catch (error) {\n console.error('quote upload failed', error)\n return sendQuoteLinkOutcome(res, { status: 500, body: { error: 'The file could not be sent. Try again.' } })\n }\n}\n\n/** `POST /api/commerce/quote-respond`. */\nexport const quoteRespondHandler: PluginApiHandler = async (req, res) =>\n sendQuoteLinkOutcome(res, await handleQuoteRespond(defaultQuoteProofDeps(), req))\n"],"names":["claimAttempt","readClientIp","VISITOR_UPLOAD_HONEYPOT_FIELD","visitorUploadFileView","notifyHostManagers","CommerceModel","orderStatusUrl","defaultQuotePayDeps","limitedQuoteLink","loadLinkedQuote","quoteLinkParams","sendQuoteLinkOutcome","quoteUploadCalls","QUOTE_PROOF_LOADS_PER_WINDOW","QUOTE_UPLOAD_CALLS_PER_WINDOW","QUOTE_RESPONSES_PER_WINDOW","defaultQuoteProofDeps","uploads","notify","args","bodyOf","req","raw","body","parsed","JSON","parse","headerOf","name","headers","value","toLowerCase","String","Array","isArray","trim","clientIp","remoteAddress","socket","reasonRefusal","status","reason","error","handleQuoteProof","deps","query","method","hostId","quoteId","token","complete","fileId","slice","limited","linked","outcome","proof","currentQuoteProofs","loaded","order","quote","find","entry","id","path","file","contentType","handleQuoteUpload","honeypot","received","storeName","refused","quoteRespondRefusal","quoteRespondRefusalMessage","scope","purpose","QUOTE_ARTWORK_PURPOSE","purposeId","caller","ip","minted","mint","request","code","retryAfterSeconds","ticket","upload","kept","finalize","UPDATED_MESSAGE","noticeOf","input","response","number","formatQuoteNumber","customer","company","email","revision","files","approving","action","payNote","requireProofApproval","details","label","filter","Boolean","join","comment","length","map","payload","type","title","link","encodeURIComponent","options","emailDetails","test","replyTo","handleQuoteRespond","idempotencyKey","hostRef","host","store","seen","Number","version","updatedAtMs","isFinite","parseQuoteResponseInput","fileIds","read","Promise","all","some","size","claimed","firestore","kind","scopeId","orgId","key","busyMessage","replay","claim","now","ref","collection","doc","settled","runTransaction","transaction","snapshot","get","current","exists","data","isQuote","quoteLinkVersion","replaced","answered","applyQuoteResponse","atMs","userAgent","set","release","view","buildQuotePageView","orderId","currency","receiptUrl","isUnpaidQuote","record","notice","console","quoteProofHandler","res","send","quoteUploadHandler","quoteRespondHandler"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { VisitorUploadFile, VisitorUploadScope } from '@aglyn/aglyn/app-utils/visitor-upload';
|
|
18
|
+
import { finalizeVisitorUpload, mintVisitorUploadTicket, type VisitorUploadResponse } from '@aglyn/tenant-data-admin/server/visitor-upload';
|
|
19
|
+
/**
|
|
20
|
+
* The visitor-upload calls a quote's routes make (AGL-4096). The routes reach
|
|
21
|
+
* this module by a dynamic import of it, so the upload pipeline, the bucket
|
|
22
|
+
* and the SVG sanitizer load only when a proof or a customer's artwork is
|
|
23
|
+
* actually sent or served, never with the routes' own modules.
|
|
24
|
+
*/
|
|
25
|
+
export interface QuoteUploadCalls {
|
|
26
|
+
mint(input: Parameters<typeof mintVisitorUploadTicket>[1]): ReturnType<typeof mintVisitorUploadTicket>;
|
|
27
|
+
finalize(input: Parameters<typeof finalizeVisitorUpload>[1]): ReturnType<typeof finalizeVisitorUpload>;
|
|
28
|
+
read(scope: VisitorUploadScope, id: string): Promise<VisitorUploadFile | null>;
|
|
29
|
+
remove(path: string): Promise<void>;
|
|
30
|
+
removeAll(scope: VisitorUploadScope): Promise<void>;
|
|
31
|
+
send(res: VisitorUploadResponse, file: Pick<VisitorUploadFile, 'path' | 'name' | 'contentType'>): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export declare function quoteUploadCalls(): QuoteUploadCalls;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { defaultVisitorUploadDeps, deleteVisitorUpload, deleteVisitorUploads, finalizeVisitorUpload, mintVisitorUploadTicket, readVisitorUpload, sendVisitorUploadFile } from "@aglyn/tenant-data-admin/server/visitor-upload";
|
|
17
|
+
export function quoteUploadCalls() {
|
|
18
|
+
const deps = defaultVisitorUploadDeps();
|
|
19
|
+
return {
|
|
20
|
+
mint: (input)=>mintVisitorUploadTicket(deps, input),
|
|
21
|
+
finalize: (input)=>finalizeVisitorUpload(deps, input),
|
|
22
|
+
read: (scope, id)=>readVisitorUpload(deps, scope, id),
|
|
23
|
+
remove: (path)=>deleteVisitorUpload(deps, path),
|
|
24
|
+
removeAll: (scope)=>deleteVisitorUploads(deps, scope),
|
|
25
|
+
send: (res, file)=>sendVisitorUploadFile(deps, res, file)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=quote-uploads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/quote-uploads.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { VisitorUploadFile, VisitorUploadScope } from '@aglyn/aglyn/app-utils/visitor-upload'\nimport {\n defaultVisitorUploadDeps,\n deleteVisitorUpload,\n deleteVisitorUploads,\n finalizeVisitorUpload,\n mintVisitorUploadTicket,\n readVisitorUpload,\n sendVisitorUploadFile,\n type VisitorUploadResponse,\n} from '@aglyn/tenant-data-admin/server/visitor-upload'\n\n/**\n * The visitor-upload calls a quote's routes make (AGL-4096). The routes reach\n * this module by a dynamic import of it, so the upload pipeline, the bucket\n * and the SVG sanitizer load only when a proof or a customer's artwork is\n * actually sent or served, never with the routes' own modules.\n */\nexport interface QuoteUploadCalls {\n mint(input: Parameters<typeof mintVisitorUploadTicket>[1]): ReturnType<typeof mintVisitorUploadTicket>\n finalize(input: Parameters<typeof finalizeVisitorUpload>[1]): ReturnType<typeof finalizeVisitorUpload>\n read(scope: VisitorUploadScope, id: string): Promise<VisitorUploadFile | null>\n remove(path: string): Promise<void>\n removeAll(scope: VisitorUploadScope): Promise<void>\n send(res: VisitorUploadResponse, file: Pick<VisitorUploadFile, 'path' | 'name' | 'contentType'>): Promise<void>\n}\n\nexport function quoteUploadCalls(): QuoteUploadCalls {\n const deps = defaultVisitorUploadDeps()\n return {\n mint: (input) => mintVisitorUploadTicket(deps, input),\n finalize: (input) => finalizeVisitorUpload(deps, input),\n read: (scope, id) => readVisitorUpload(deps, scope, id),\n remove: (path) => deleteVisitorUpload(deps, path),\n removeAll: (scope) => deleteVisitorUploads(deps, scope),\n send: (res, file) => sendVisitorUploadFile(deps, res, file),\n }\n}\n"],"names":["defaultVisitorUploadDeps","deleteVisitorUpload","deleteVisitorUploads","finalizeVisitorUpload","mintVisitorUploadTicket","readVisitorUpload","sendVisitorUploadFile","quoteUploadCalls","deps","mint","input","finalize","read","scope","id","remove","path","removeAll","send","res","file"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SACEA,wBAAwB,EACxBC,mBAAmB,EACnBC,oBAAoB,EACpBC,qBAAqB,EACrBC,uBAAuB,EACvBC,iBAAiB,EACjBC,qBAAqB,QAEhB,iDAAgD;AAiBvD,OAAO,SAASC;IACd,MAAMC,OAAOR;IACb,OAAO;QACLS,MAAM,CAACC,QAAUN,wBAAwBI,MAAME;QAC/CC,UAAU,CAACD,QAAUP,sBAAsBK,MAAME;QACjDE,MAAM,CAACC,OAAOC,KAAOT,kBAAkBG,MAAMK,OAAOC;QACpDC,QAAQ,CAACC,OAASf,oBAAoBO,MAAMQ;QAC5CC,WAAW,CAACJ,QAAUX,qBAAqBM,MAAMK;QACjDK,MAAM,CAACC,KAAKC,OAASd,sBAAsBE,MAAMW,KAAKC;IACxD;AACF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type PluginApiHandler, type PluginApiRequest } from '@aglyn/aglyn/server';
|
|
18
|
+
import { type PluginPersonRecord, type PluginPersonSearchRequest } from '@aglyn/aglyn/plugin-manager/plugin-person-records';
|
|
19
|
+
import { type SaleTaxEngineRequest, type SaleTaxEngineResult } from './tax-engine-quote';
|
|
20
|
+
import { type CheckoutSessionExpiry } from './checkout-session-expiry';
|
|
21
|
+
import type { quotePdfFile } from './commerce-pdf';
|
|
22
|
+
import { type CommercePdfFile } from './pdf-response';
|
|
23
|
+
import type { QuoteUploadCalls } from './quote-uploads';
|
|
24
|
+
export interface QuoteDeps {
|
|
25
|
+
verifyIdToken(token: string): Promise<{
|
|
26
|
+
uid: string;
|
|
27
|
+
email?: string | null;
|
|
28
|
+
}>;
|
|
29
|
+
firestore(): FirebaseFirestore.Firestore;
|
|
30
|
+
/** The workspace that owns the site, for its plan. */
|
|
31
|
+
orgForHost(hostId: string): Promise<{
|
|
32
|
+
id?: string;
|
|
33
|
+
org?: unknown;
|
|
34
|
+
} | null>;
|
|
35
|
+
searchPeople(request: PluginPersonSearchRequest): Promise<PluginPersonRecord[] | null>;
|
|
36
|
+
/** The site's tax service, where one is connected; `{ quote: null }` otherwise. */
|
|
37
|
+
quoteTax(request: SaleTaxEngineRequest): Promise<SaleTaxEngineResult>;
|
|
38
|
+
/** Closes a Checkout Session the pay page opened for the quote. */
|
|
39
|
+
expireSession(sessionId: string): Promise<CheckoutSessionExpiry>;
|
|
40
|
+
/** The quote's pay link at a link version, or null when the site has no address or no signing secret. */
|
|
41
|
+
payLinkUrl(host: Record<string, unknown>, hostId: string, quoteId: string, version: number): string | null;
|
|
42
|
+
/** The quote's PDF; loads the PDF writer and sharp only when asked. */
|
|
43
|
+
quotePdf(input: Parameters<typeof quotePdfFile>[0]): Promise<CommercePdfFile>;
|
|
44
|
+
/** The upload pipeline, loaded only by the proof actions. */
|
|
45
|
+
uploads(): Promise<QuoteUploadCalls>;
|
|
46
|
+
now(): number;
|
|
47
|
+
}
|
|
48
|
+
export declare function defaultQuoteDeps(): QuoteDeps;
|
|
49
|
+
type Outcome = {
|
|
50
|
+
status: number;
|
|
51
|
+
body: Record<string, unknown>;
|
|
52
|
+
pdf?: CommercePdfFile;
|
|
53
|
+
/** A proof or a customer's file, streamed by the handler. */
|
|
54
|
+
file?: {
|
|
55
|
+
path: string;
|
|
56
|
+
name: string;
|
|
57
|
+
contentType: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export declare function handleQuote(deps: QuoteDeps, req: PluginApiRequest): Promise<Outcome>;
|
|
61
|
+
/** `POST /api/commerce/quote`. */
|
|
62
|
+
export declare const quoteHandler: PluginApiHandler;
|
|
63
|
+
export {};
|