@tokenoftrust/storefront-runner 2.4.53 → 2.4.55
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/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +203 -1
- package/apps/storefront/src/lib/apps/orders/orderMapper.ts +321 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +331 -1
- package/apps/storefront/src/lib/fulfillment/fulfillmentService.ts +1654 -0
- package/apps/storefront/src/lib/fulfillment/readiness.ts +150 -0
- package/apps/storefront/src/lib/fulfillment/store.ts +109 -0
- package/apps/storefront/src/lib/webhooks/deliveryEngine.ts +110 -1
- package/apps/storefront/src/lib/webhooks/deliveryStore.ts +70 -0
- package/apps/storefront/src/lib/webhooks/getDispatcher.ts +29 -2
- package/apps/storefront/src/pages/api/admin/fulfillment/batches.ts +59 -0
- package/apps/storefront/src/pages/api/admin/fulfillment/orders/[id]/address.ts +32 -0
- package/apps/storefront/src/pages/api/admin/fulfillment/orders/[id].ts +117 -0
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authenticated owner API for one order's fulfillment commands.
|
|
3
|
+
* The response is state codes and the fulfillment view. It never returns an address.
|
|
4
|
+
*/
|
|
5
|
+
import type { APIContext } from "astro";
|
|
6
|
+
import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
|
|
7
|
+
import {
|
|
8
|
+
fulfillmentResultResponse,
|
|
9
|
+
isOpaqueId,
|
|
10
|
+
openFulfillment,
|
|
11
|
+
parseFulfillmentBody,
|
|
12
|
+
readJsonBody,
|
|
13
|
+
type FulfillmentCommandOptions,
|
|
14
|
+
type LabelOutcomeInput,
|
|
15
|
+
} from "@/lib/fulfillment/fulfillmentService";
|
|
16
|
+
|
|
17
|
+
export const prerender = false;
|
|
18
|
+
|
|
19
|
+
function ownerRefusal(status: number, reason: string): Response {
|
|
20
|
+
return new Response(JSON.stringify({ error: reason }), {
|
|
21
|
+
status,
|
|
22
|
+
headers: {
|
|
23
|
+
"content-type": "application/json; charset=utf-8",
|
|
24
|
+
"cache-control": "no-store",
|
|
25
|
+
"x-robots-tag": "noindex, nofollow",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function requireOwner(context: APIContext): Promise<Response | null> {
|
|
31
|
+
const access = await requireAdminApiEntry(context, { requireOwner: true });
|
|
32
|
+
if (access.ok) return null;
|
|
33
|
+
return ownerRefusal(access.status, access.reason);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ACTIONS = new Set([
|
|
37
|
+
"pick",
|
|
38
|
+
"pack",
|
|
39
|
+
"release",
|
|
40
|
+
"hold",
|
|
41
|
+
"clear-hold",
|
|
42
|
+
"cancel",
|
|
43
|
+
"refund",
|
|
44
|
+
"label-outcome",
|
|
45
|
+
"hand-off",
|
|
46
|
+
"return",
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
function fail(code: string): Response {
|
|
50
|
+
return fulfillmentResultResponse({ ok: false, code }, () => ({}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function GET(context: APIContext): Promise<Response> {
|
|
54
|
+
const refused = await requireOwner(context);
|
|
55
|
+
if (refused) return refused;
|
|
56
|
+
const orderId = context.params.id ?? "";
|
|
57
|
+
if (!isOpaqueId(orderId)) return fail("not-found");
|
|
58
|
+
const opened = await openFulfillment(context);
|
|
59
|
+
if (!opened.ok) return opened.response;
|
|
60
|
+
return fulfillmentResultResponse(await opened.service.get(opened.actor, orderId), (view) => view);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
64
|
+
const refused = await requireOwner(context);
|
|
65
|
+
if (refused) return refused;
|
|
66
|
+
const orderId = context.params.id ?? "";
|
|
67
|
+
if (!isOpaqueId(orderId)) return fail("not-found");
|
|
68
|
+
const raw = await readJsonBody(context.request);
|
|
69
|
+
if (!raw.ok) return fulfillmentResultResponse(raw, () => ({}));
|
|
70
|
+
const parsed = parseFulfillmentBody(raw.value);
|
|
71
|
+
if (!parsed.ok) return fulfillmentResultResponse(parsed, () => ({}));
|
|
72
|
+
if (!ACTIONS.has(parsed.value.action)) return fail("malformed");
|
|
73
|
+
const opened = await openFulfillment(context, parsed.value.tenantId ?? null);
|
|
74
|
+
if (!opened.ok) return opened.response;
|
|
75
|
+
const { service, actor } = opened;
|
|
76
|
+
const options: FulfillmentCommandOptions = {
|
|
77
|
+
...(parsed.value.expectedVersion !== undefined ? { expectedVersion: parsed.value.expectedVersion } : {}),
|
|
78
|
+
...(parsed.value.scope !== undefined ? { scope: parsed.value.scope } : {}),
|
|
79
|
+
...(parsed.value.carrier !== undefined ? { carrier: parsed.value.carrier } : {}),
|
|
80
|
+
...(parsed.value.lines !== undefined ? { lines: parsed.value.lines } : {}),
|
|
81
|
+
};
|
|
82
|
+
switch (parsed.value.action) {
|
|
83
|
+
case "pick":
|
|
84
|
+
return fulfillmentResultResponse(await service.pick(actor, orderId, options), (view) => view);
|
|
85
|
+
case "pack":
|
|
86
|
+
return fulfillmentResultResponse(await service.pack(actor, orderId, options), (view) => view);
|
|
87
|
+
case "release":
|
|
88
|
+
return fulfillmentResultResponse(await service.release(actor, orderId, options), (view) => view);
|
|
89
|
+
case "hold":
|
|
90
|
+
return fulfillmentResultResponse(await service.hold(actor, orderId, options), (view) => view);
|
|
91
|
+
case "clear-hold":
|
|
92
|
+
return fulfillmentResultResponse(await service.clearOperatorHold(actor, orderId, options), (view) => view);
|
|
93
|
+
case "cancel":
|
|
94
|
+
return fulfillmentResultResponse(await service.requestCancel(actor, orderId, options), (view) => view);
|
|
95
|
+
case "refund":
|
|
96
|
+
return fulfillmentResultResponse(await service.requestRefund(actor, orderId, options), (view) => view);
|
|
97
|
+
case "hand-off":
|
|
98
|
+
return fulfillmentResultResponse(await service.handOff(actor, orderId, options), (view) => view);
|
|
99
|
+
case "return":
|
|
100
|
+
return fulfillmentResultResponse(service.createReturn(actor, orderId), () => ({}));
|
|
101
|
+
case "label-outcome": {
|
|
102
|
+
const outcome = parsed.value.outcome;
|
|
103
|
+
const adultSignature = parsed.value.adultSignature;
|
|
104
|
+
if (outcome !== "purchased" && outcome !== "unknown" && outcome !== "voided") return fail("malformed");
|
|
105
|
+
if (adultSignature !== "accepted" && adultSignature !== "refused" && adultSignature !== "absent") return fail("malformed");
|
|
106
|
+
const input: LabelOutcomeInput = {
|
|
107
|
+
outcome,
|
|
108
|
+
adultSignature,
|
|
109
|
+
...(parsed.value.carrier !== undefined ? { carrier: parsed.value.carrier } : {}),
|
|
110
|
+
...(parsed.value.expectedVersion !== undefined ? { expectedVersion: parsed.value.expectedVersion } : {}),
|
|
111
|
+
};
|
|
112
|
+
return fulfillmentResultResponse(await service.applyLabelOutcome(actor, orderId, input), (view) => view);
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
return fail("malformed");
|
|
116
|
+
}
|
|
117
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.55",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|