@tokenoftrust/storefront-runner 2.4.64 → 2.4.66
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/admin/services/mock/index.ts +0 -1
- package/apps/storefront/src/lib/admin/viewmodels/fulfillment.ts +4 -114
- package/apps/storefront/src/lib/webhooks/getDispatcher.ts +1 -3
- package/apps/storefront/src/pages/api/admin/commerce-readiness.ts +2 -5
- package/package.json +1 -1
- package/apps/storefront/src/lib/admin/services/mock/fulfillment.ts +0 -19
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { getOrdersViewModel } from "./orders";
|
|
2
2
|
export { getProductsViewModel } from "./products";
|
|
3
3
|
export { getCustomersViewModel } from "./customers";
|
|
4
|
-
export { getFulfillmentViewModel } from "./fulfillment";
|
|
5
4
|
export { getReportingViewModel } from "./reporting";
|
|
6
5
|
export { getSettingsViewModel } from "./settings";
|
|
7
6
|
export { getCheckoutSettingsViewModel } from "./checkoutSettings";
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Daily batch desk. Queues, evidence, and commands are a pure projection of the
|
|
3
|
-
* commerce contract.
|
|
4
|
-
*
|
|
5
|
-
* log never carry a destination.
|
|
3
|
+
* commerce contract. Print payload and tracking links are owner-only. Public
|
|
4
|
+
* events and the desk log never carry a destination.
|
|
6
5
|
*/
|
|
7
6
|
import {
|
|
8
7
|
actorMayCommand,
|
|
@@ -224,25 +223,6 @@ export interface WorkspaceCommand {
|
|
|
224
223
|
quantityInvalid: boolean;
|
|
225
224
|
lineId: boolean;
|
|
226
225
|
exception: string | null;
|
|
227
|
-
accountConfigured: boolean | null;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export interface WorkspaceHttpRequest {
|
|
231
|
-
method: string;
|
|
232
|
-
url: string;
|
|
233
|
-
body: string | null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export interface WorkspaceHttpResponse {
|
|
237
|
-
status: number;
|
|
238
|
-
contentType: string;
|
|
239
|
-
body: string;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export interface TestFulfillmentAdapter {
|
|
243
|
-
readonly livePurchase: false;
|
|
244
|
-
state(): WorkspaceState;
|
|
245
|
-
handle(request: WorkspaceHttpRequest): WorkspaceHttpResponse;
|
|
246
226
|
}
|
|
247
227
|
|
|
248
228
|
export function defaultWorkspaceQuery(): WorkspaceQuery {
|
|
@@ -842,17 +822,6 @@ export function applyWorkspaceCommand(input: WorkspaceState, command: WorkspaceC
|
|
|
842
822
|
ids.map((orderId) => blankResult(command.command, orderId, "refused", "partial-shipment")),
|
|
843
823
|
);
|
|
844
824
|
}
|
|
845
|
-
if (command.command === "set-carrier-account") {
|
|
846
|
-
const carrierReason = explicitCarrierReason(command.carrier);
|
|
847
|
-
if (carrierReason) return finishCommand(state, command.actorName, [blankResult(command.command, "", "refused", carrierReason)]);
|
|
848
|
-
if (command.accountConfigured === null) {
|
|
849
|
-
return finishCommand(state, command.actorName, [blankResult(command.command, "", "refused", "carrier-account")]);
|
|
850
|
-
}
|
|
851
|
-
state.carrierAccountConfigured = command.accountConfigured;
|
|
852
|
-
return finishCommand(state, command.actorName, [
|
|
853
|
-
blankResult(command.command, "", "admitted", command.accountConfigured ? "configured" : "missing"),
|
|
854
|
-
]);
|
|
855
|
-
}
|
|
856
825
|
if (command.orderIds.length === 0) {
|
|
857
826
|
return finishCommand(state, command.actorName, [blankResult(command.command, "", "refused", "empty-batch")]);
|
|
858
827
|
}
|
|
@@ -883,7 +852,6 @@ const COMMANDS = new Set([
|
|
|
883
852
|
"pick-batch",
|
|
884
853
|
"packing-slip",
|
|
885
854
|
"record-exception",
|
|
886
|
-
"set-carrier-account",
|
|
887
855
|
"refund",
|
|
888
856
|
"return",
|
|
889
857
|
]);
|
|
@@ -922,8 +890,6 @@ export function parseWorkspaceCommand(params: URLSearchParams): { ok: true; comm
|
|
|
922
890
|
else quantity = Number(raw);
|
|
923
891
|
}
|
|
924
892
|
}
|
|
925
|
-
const configuredRaw = params.get("configured");
|
|
926
|
-
const accountConfigured = configuredRaw === "1" ? true : configuredRaw === "0" ? false : null;
|
|
927
893
|
const carrierRaw = params.get("carrier");
|
|
928
894
|
const purchaseRaw = params.get("purchaseKey");
|
|
929
895
|
return {
|
|
@@ -938,66 +904,6 @@ export function parseWorkspaceCommand(params: URLSearchParams): { ok: true; comm
|
|
|
938
904
|
quantityInvalid,
|
|
939
905
|
lineId: params.has("lineId"),
|
|
940
906
|
exception: params.get("exception"),
|
|
941
|
-
accountConfigured,
|
|
942
|
-
},
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
export function handleWorkspaceRequest(
|
|
947
|
-
state: WorkspaceState,
|
|
948
|
-
request: WorkspaceHttpRequest,
|
|
949
|
-
): { state: WorkspaceState; response: WorkspaceHttpResponse } {
|
|
950
|
-
let url: URL;
|
|
951
|
-
try {
|
|
952
|
-
url = new URL(request.url, "http://127.0.0.1");
|
|
953
|
-
} catch {
|
|
954
|
-
return { state, response: { status: 400, contentType: "text/plain; charset=utf-8", body: "bad url" } };
|
|
955
|
-
}
|
|
956
|
-
if (url.pathname !== SCRAPPY_FULFILLMENT_PATH) {
|
|
957
|
-
return { state, response: { status: 404, contentType: "text/plain; charset=utf-8", body: "not found" } };
|
|
958
|
-
}
|
|
959
|
-
const method = request.method.toUpperCase();
|
|
960
|
-
if (method === "GET") {
|
|
961
|
-
const view = projectWorkspace(state, parseWorkspaceQuery(url.searchParams));
|
|
962
|
-
return {
|
|
963
|
-
state,
|
|
964
|
-
response: { status: 200, contentType: "text/html; charset=utf-8", body: renderFulfillmentDocument(view) },
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
if (method === "POST") {
|
|
968
|
-
const params = new URLSearchParams(request.body ?? "");
|
|
969
|
-
const parsed = parseWorkspaceCommand(params);
|
|
970
|
-
if (!parsed.ok) {
|
|
971
|
-
return {
|
|
972
|
-
state,
|
|
973
|
-
response: {
|
|
974
|
-
status: 400,
|
|
975
|
-
contentType: "text/html; charset=utf-8",
|
|
976
|
-
body: `<!doctype html><p data-command-error="${esc(parsed.error)}">Unknown command.</p>`,
|
|
977
|
-
},
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
const next = applyWorkspaceCommand(state, parsed.command);
|
|
981
|
-
const query = parseWorkspaceQuery(params);
|
|
982
|
-
query.cursor = null;
|
|
983
|
-
const view = projectWorkspace(next, query);
|
|
984
|
-
return {
|
|
985
|
-
state: next,
|
|
986
|
-
response: { status: 200, contentType: "text/html; charset=utf-8", body: renderFulfillmentDocument(view) },
|
|
987
|
-
};
|
|
988
|
-
}
|
|
989
|
-
return { state, response: { status: 405, contentType: "text/plain; charset=utf-8", body: "method" } };
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
export function createTestFulfillmentAdapter(tenantId = "synthetic-tenant", stage: CommerceStage = "preview"): TestFulfillmentAdapter {
|
|
993
|
-
let state = initialWorkspaceState(tenantId, stage);
|
|
994
|
-
return {
|
|
995
|
-
livePurchase: false,
|
|
996
|
-
state: () => state,
|
|
997
|
-
handle(request) {
|
|
998
|
-
const outcome = handleWorkspaceRequest(state, request);
|
|
999
|
-
state = outcome.state;
|
|
1000
|
-
return outcome.response;
|
|
1001
907
|
},
|
|
1002
908
|
};
|
|
1003
909
|
}
|
|
@@ -1041,10 +947,6 @@ function hiddenFields(view: FulfillmentViewModel, command: string, extras: Array
|
|
|
1041
947
|
.join("");
|
|
1042
948
|
}
|
|
1043
949
|
|
|
1044
|
-
function resultItem(result: WorkspaceResult): string {
|
|
1045
|
-
return `<li data-result-order="${esc(result.orderId)}" data-result-command="${esc(result.command)}" data-result-status="${result.status}" data-result-reason="${esc(result.reason)}" data-cost-minor="${result.costMinor ?? ""}" data-purchase-id="${esc(result.purchaseId ?? "")}" data-live-purchase="false" data-release-reservation="${result.releaseReservation ? "true" : "false"}" data-restock="false" data-blind-repurchase="false">${esc(result.command)} ${esc(result.orderId)} ${result.status} ${esc(result.reason)}</li>`;
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
950
|
function orderActions(view: FulfillmentViewModel, order: WorkspaceOrderView, action: string): string {
|
|
1049
951
|
const purchaseExtras: Array<[string, string]> = [["orderId", order.id]];
|
|
1050
952
|
if (order.purchaseKey !== null) purchaseExtras.push(["purchaseKey", order.purchaseKey]);
|
|
@@ -1075,10 +977,6 @@ export function renderFulfillmentWorkspace(view: FulfillmentViewModel): string {
|
|
|
1075
977
|
return `<a data-queue-link="${queue}" href="${esc(href(view, { queue, cursor: null }))}">${esc(label)}${count}</a>`;
|
|
1076
978
|
})
|
|
1077
979
|
.join("");
|
|
1078
|
-
const actorLinks = ACTOR_SPECS.map(
|
|
1079
|
-
(spec) =>
|
|
1080
|
-
`<a data-actor-link="${spec.name}" href="${esc(href(view, { actorName: spec.name, cursor: null }))}">${esc(spec.name)}</a>`,
|
|
1081
|
-
).join("");
|
|
1082
980
|
const action = esc(href(view, { cursor: null }));
|
|
1083
981
|
const rows = view.orders
|
|
1084
982
|
.map((order) => {
|
|
@@ -1134,12 +1032,11 @@ export function renderFulfillmentWorkspace(view: FulfillmentViewModel): string {
|
|
|
1134
1032
|
? `<a data-page-next href="${esc(href(view, { cursor: view.nextCursor }))}">Next page</a>`
|
|
1135
1033
|
: "";
|
|
1136
1034
|
const account = view.carrierAccountConfigured ? "configured" : "missing";
|
|
1137
|
-
return `<div data-scrappy-batch data-target-tenant="${esc(view.targetTenant)}" data-
|
|
1035
|
+
return `<div data-scrappy-batch data-target-tenant="${esc(view.targetTenant)}" data-live-purchase="false" data-carrier-account="${account}" data-prohibited-carrier="USPS" data-adult-signature-required="true" data-partial-shipment="unavailable" data-returns="unavailable" data-blind-repurchase="false" data-actor="${esc(view.actorName)}" data-actor-role="${esc(view.actorRole)}" data-storewide-ready="${view.storewide.ready}" data-storewide-held="${view.storewide.held}" data-storewide-in-progress="${view.storewide.in_progress}" data-storewide-shipped="${view.storewide.shipped}" data-filter-count="${view.filterCount}" data-page-count="${view.pageCount}" data-queue="${esc(view.queue)}">
|
|
1138
1036
|
<h2 id="fulfillment-title">Daily batch</h2>
|
|
1139
|
-
<p>
|
|
1037
|
+
<p>Not a live label purchase. Carrier account is configuration. USPS is prohibited. Adult signature is required. Shipment is the whole order. Partial shipment is not available. Returns are not automated.</p>
|
|
1140
1038
|
<p data-carrier-account-text>${view.carrierAccountConfigured ? "Carrier account: configured." : "Carrier account: not configured. Do not choose a carrier."}</p>
|
|
1141
1039
|
<p><a data-refresh href="${esc(href(view))}">Refresh</a></p>
|
|
1142
|
-
<nav data-actor-switch>${actorLinks}</nav>
|
|
1143
1040
|
<nav data-queue-switch>${queueLinks}</nav>
|
|
1144
1041
|
<form method="get" action="${SCRAPPY_FULFILLMENT_PATH}">
|
|
1145
1042
|
<input type="hidden" name="actor" value="${esc(view.actorName)}">
|
|
@@ -1164,12 +1061,5 @@ export function renderFulfillmentWorkspace(view: FulfillmentViewModel): string {
|
|
|
1164
1061
|
${next}
|
|
1165
1062
|
<section data-pick-list><h3>Pick list</h3><ul>${pickItems}</ul></section>
|
|
1166
1063
|
${details}
|
|
1167
|
-
<ol data-last-results>${view.lastResults.map(resultItem).join("")}</ol>
|
|
1168
|
-
<section data-public-events>${view.events.map((event) => `<pre>${esc(JSON.stringify(event))}</pre>`).join("")}</section>
|
|
1169
|
-
<pre data-adapter-log>${esc(view.log.join("\n"))}</pre>
|
|
1170
1064
|
</div>`;
|
|
1171
1065
|
}
|
|
1172
|
-
|
|
1173
|
-
export function renderFulfillmentDocument(view: FulfillmentViewModel): string {
|
|
1174
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Daily batch</title></head><body><section id="fulfillment" data-admin-panel data-agent-scope="fulfillment">${renderFulfillmentWorkspace(view)}</section></body></html>`;
|
|
1175
|
-
}
|
|
@@ -66,9 +66,7 @@ export class CommerceAlarmDispatcher {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
export async function getReliableDispatcher(): Promise<CommerceAlarmDispatcher | Response> {
|
|
69
|
-
const scheduler = await readEnv("SCRAPPY_COMMERCE_SCHEDULER");
|
|
70
|
-
if (scheduler !== COMMERCE_SCHEDULER) return json({ error: "commerce scheduler not configured" }, 503);
|
|
71
69
|
const d1 = await readD1("STOREFRONT_APPS_DB");
|
|
72
70
|
if (!d1) return json({ error: "commerce scheduler not configured" }, 503);
|
|
73
|
-
return new CommerceAlarmDispatcher(fromD1(d1),
|
|
71
|
+
return new CommerceAlarmDispatcher(fromD1(d1), COMMERCE_SCHEDULER);
|
|
74
72
|
}
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import type { APIContext } from "astro";
|
|
12
12
|
import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
|
|
13
13
|
import { workerCommit } from "@/lib/activity/workerCommit";
|
|
14
|
-
import { readEnv } from "@/lib/env";
|
|
15
14
|
import {
|
|
16
15
|
COMMERCE_SCHEDULER,
|
|
17
16
|
actorMayCommand,
|
|
@@ -107,8 +106,7 @@ export function admitAdminReplay(input: {
|
|
|
107
106
|
export async function GET(context: APIContext): Promise<Response> {
|
|
108
107
|
const access = await requireAdminApiEntry(context, { requireOwner: true });
|
|
109
108
|
if (!access.ok) return json({ ok: false, reason: access.reason }, access.status);
|
|
110
|
-
|
|
111
|
-
return json({ ok: true, report: projectAdminReadiness({ scheduler, deployedSha: workerCommit() }) });
|
|
109
|
+
return json({ ok: true, report: projectAdminReadiness({ scheduler: COMMERCE_SCHEDULER, deployedSha: workerCommit() }) });
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
export async function POST(context: APIContext): Promise<Response> {
|
|
@@ -133,13 +131,12 @@ export async function POST(context: APIContext): Promise<Response> {
|
|
|
133
131
|
if (!OPAQUE.test(record.orderId) || !OPAQUE.test(record.sourceEventId)) return json({ ok: false, reason: "refused" }, 400);
|
|
134
132
|
const decision = admitAdminReplay({ tenantId: context.locals.tenant.tenant_id, stage, kind });
|
|
135
133
|
if (!decision.admitted) return json({ ok: false, reason: decision.reason }, 403);
|
|
136
|
-
const scheduler = await readEnv("SCRAPPY_COMMERCE_SCHEDULER");
|
|
137
134
|
return json({
|
|
138
135
|
ok: true,
|
|
139
136
|
admitted: true,
|
|
140
137
|
durableWrite: false,
|
|
141
138
|
command: "reconcile",
|
|
142
139
|
reason: decision.reason,
|
|
143
|
-
report: projectAdminReadiness({ scheduler, deployedSha: workerCommit() }),
|
|
140
|
+
report: projectAdminReadiness({ scheduler: COMMERCE_SCHEDULER, deployedSha: workerCommit() }),
|
|
144
141
|
});
|
|
145
142
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.66",
|
|
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",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { AdminTenantEnvelope } from "../../envelope";
|
|
2
|
-
import { memoizeByEnvelope } from "../../memoizeByEnvelope";
|
|
3
|
-
import {
|
|
4
|
-
defaultWorkspaceQuery,
|
|
5
|
-
initialWorkspaceState,
|
|
6
|
-
projectWorkspace,
|
|
7
|
-
type FulfillmentViewModel,
|
|
8
|
-
} from "../../viewmodels/fulfillment";
|
|
9
|
-
|
|
10
|
-
/** Initial test-adapter snapshot. Not a seeded queue and not a live order feed. */
|
|
11
|
-
export function readTestAdapterSnapshot(targetTenant: string): FulfillmentViewModel {
|
|
12
|
-
return projectWorkspace(initialWorkspaceState(targetTenant), defaultWorkspaceQuery());
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function computeFulfillmentViewModel(envelope: AdminTenantEnvelope): FulfillmentViewModel {
|
|
16
|
-
return readTestAdapterSnapshot(envelope.targetTenant);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const getFulfillmentViewModel = memoizeByEnvelope(computeFulfillmentViewModel);
|