punchout-simulator 0.5.2 → 0.6.1
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 +43 -3
- package/dist/server/cli.js +96 -16
- package/dist/web/assets/{cssMode-BNnlXFNU.js → cssMode-DipgJtey.js} +1 -1
- package/dist/web/assets/{freemarker2-BuNAmJWP.js → freemarker2-Broesno_.js} +1 -1
- package/dist/web/assets/{handlebars-BJ-VFQ5X.js → handlebars-CjP6B5LA.js} +1 -1
- package/dist/web/assets/{html-BkFoYEs4.js → html-DRw6NWlS.js} +1 -1
- package/dist/web/assets/{htmlMode-DamCYXd_.js → htmlMode-CRd2lx43.js} +1 -1
- package/dist/web/assets/{index-BMoN8EG3.css → index-BZV9nM3Z.css} +1 -1
- package/dist/web/assets/{index-CR8XUPcx.js → index-ByIxIbJu.js} +205 -205
- package/dist/web/assets/{javascript-IrwRCTVa.js → javascript-C_i2lhIv.js} +1 -1
- package/dist/web/assets/{jsonMode-BGCKIu47.js → jsonMode-BgryI0EY.js} +1 -1
- package/dist/web/assets/{liquid-D5qP5QxV.js → liquid-7FQOG1B6.js} +1 -1
- package/dist/web/assets/{mdx-DPwh6PUj.js → mdx-qBzkPJqp.js} +1 -1
- package/dist/web/assets/{python-DlzVHyu7.js → python-B4DngJbm.js} +1 -1
- package/dist/web/assets/{razor-D8NvtH9c.js → razor-C_lSlak7.js} +1 -1
- package/dist/web/assets/{tsMode-CJgtFdcK.js → tsMode-Xnz0Jk77.js} +1 -1
- package/dist/web/assets/{typescript-BNSC8ixL.js → typescript-d2XcegVU.js} +1 -1
- package/dist/web/assets/{xml-C_t3QDrI.js → xml-D_x-LgOL.js} +1 -1
- package/dist/web/assets/{yaml-BM1u2RtM.js → yaml-gU-APWSC.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,13 @@ It is role-neutral and runs in two modes (mirror images of each other):
|
|
|
18
18
|
|
|
19
19
|
Protocol scope: **cXML only**.
|
|
20
20
|
|
|
21
|
+
> **New to PunchOut, or want the full picture?** Read the in-depth reports at
|
|
22
|
+
> [**slawomir-szostak.github.io/punchout-simulator**](https://slawomir-szostak.github.io/punchout-simulator/):
|
|
23
|
+
> the [PunchOut business primer](https://slawomir-szostak.github.io/punchout-simulator/punchout-business-primer_en.html),
|
|
24
|
+
> the [Architecture reference](https://slawomir-szostak.github.io/punchout-simulator/architecture_en.html),
|
|
25
|
+
> and the [Operations & usage guide](https://slawomir-szostak.github.io/punchout-simulator/operations-guide_en.html).
|
|
26
|
+
> (Markdown sources live in [`docs/`](docs/).)
|
|
27
|
+
|
|
21
28
|
---
|
|
22
29
|
|
|
23
30
|
## Quick start
|
|
@@ -30,15 +37,17 @@ That's it — no install, no build. It boots a local server, opens your browser,
|
|
|
30
37
|
and seeds a **built-in demo**: a virtual buyer wired to a built-in mock supplier,
|
|
31
38
|
so you can run the entire roundtrip immediately:
|
|
32
39
|
|
|
33
|
-
1.
|
|
40
|
+
1. On the **Sessions** tab, click **+ New session**, pick the **Demo Buyer → Demo
|
|
41
|
+
Supplier** connection and the **create** operation.
|
|
34
42
|
2. **Send SetupRequest** → the mock supplier replies with a StartPage.
|
|
35
43
|
3. **Open the catalog**, set quantities, **return the cart** — the punchback
|
|
36
44
|
lands back in the app live.
|
|
37
45
|
4. **Build the OrderRequest**, edit the cXML if you want (tweak `<Comments>`,
|
|
38
46
|
addresses, attachment refs), optionally attach files at the **order or item
|
|
39
47
|
level**, optionally flip on the **dangling-`cid` test**, then **send it** and
|
|
40
|
-
inspect the supplier's response.
|
|
41
|
-
|
|
48
|
+
inspect the supplier's response. (A **Validate** button lints the cXML before
|
|
49
|
+
you send.)
|
|
50
|
+
5. If validation fails, **edit and re-send** — the session keeps its log and cart.
|
|
42
51
|
|
|
43
52
|
Every document — inbound and outbound — is validated and logged.
|
|
44
53
|
|
|
@@ -142,6 +151,27 @@ receiver detects the missing attachment. `<Comments>` (and therefore
|
|
|
142
151
|
|
|
143
152
|
---
|
|
144
153
|
|
|
154
|
+
## Sessions
|
|
155
|
+
|
|
156
|
+
A **session** is one PunchOut conversation, keyed by its `BuyerCookie`:
|
|
157
|
+
SetupRequest → catalog → cart → OrderRequest → OrderResponse. The **Sessions** tab
|
|
158
|
+
(under **Run**, separate from the **Configure** sections) is the workspace:
|
|
159
|
+
|
|
160
|
+
- A live **session list** — Mode-A sessions you start *and* Mode-B sessions an
|
|
161
|
+
external buyer initiates against the tool's endpoints (those show as **inbound**).
|
|
162
|
+
- A per-session **message log** (each session keeps its own; no global firehose).
|
|
163
|
+
- **+ New session** picks a connection and the SetupRequest **operation**:
|
|
164
|
+
- **create** — a fresh, empty cart (the default).
|
|
165
|
+
- **edit** — reopen a previous session's returned cart for modification; its
|
|
166
|
+
items are sent as `ItemOut` inside the SetupRequest (`operation="edit"`).
|
|
167
|
+
- **inspect** — view a previously ordered item read-only (`operation="inspect"`),
|
|
168
|
+
carrying that item (or the whole source cart) as `ItemOut`.
|
|
169
|
+
|
|
170
|
+
Connections/Buyers/Suppliers/Products/Profiles remain under **Configure** — you set
|
|
171
|
+
them up there, then run them from **Sessions**.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
145
175
|
## Network reachability
|
|
146
176
|
|
|
147
177
|
- **Mode A rarely needs a tunnel.** The punchback is a browser auto-submit from
|
|
@@ -176,6 +206,16 @@ src/
|
|
|
176
206
|
└─ cxml/ build · parse · validate · multipart · types
|
|
177
207
|
```
|
|
178
208
|
|
|
209
|
+
For a deeper treatment — component breakdown, the full data model, and Mode A /
|
|
210
|
+
Mode B sequence diagrams — see the
|
|
211
|
+
[**Architecture reference**](https://slawomir-szostak.github.io/punchout-simulator/architecture_en.html).
|
|
212
|
+
Alongside it: a [**PunchOut business primer**](https://slawomir-szostak.github.io/punchout-simulator/punchout-business-primer_en.html)
|
|
213
|
+
(the business process and where this tool fits) and an
|
|
214
|
+
[**Operations & usage guide**](https://slawomir-szostak.github.io/punchout-simulator/operations-guide_en.html)
|
|
215
|
+
(sessions, operations, profiles, product lists, validation). These render at
|
|
216
|
+
[slawomir-szostak.github.io/punchout-simulator](https://slawomir-szostak.github.io/punchout-simulator/);
|
|
217
|
+
their canonical Markdown sources live in [`docs/`](docs/).
|
|
218
|
+
|
|
179
219
|
### Data model
|
|
180
220
|
|
|
181
221
|
The config is normalized into five entities:
|
package/dist/server/cli.js
CHANGED
|
@@ -869,7 +869,8 @@ import { Hono as Hono5 } from "hono";
|
|
|
869
869
|
import { nanoid as nanoid5 } from "nanoid";
|
|
870
870
|
|
|
871
871
|
// src/server/store/log.ts
|
|
872
|
-
import { appendFileSync, existsSync, readdirSync, readFileSync } from "fs";
|
|
872
|
+
import { appendFileSync, existsSync, readdirSync, readFileSync, rmSync } from "fs";
|
|
873
|
+
import { resolve as resolve2, sep } from "path";
|
|
873
874
|
import { nanoid as nanoid2 } from "nanoid";
|
|
874
875
|
|
|
875
876
|
// src/server/bus.ts
|
|
@@ -917,6 +918,13 @@ function readSession(sessionId) {
|
|
|
917
918
|
}
|
|
918
919
|
}).filter((r) => r !== null);
|
|
919
920
|
}
|
|
921
|
+
function deleteSession(sessionId) {
|
|
922
|
+
const file = resolve2(sessionFile(sessionId));
|
|
923
|
+
if (!file.startsWith(resolve2(sessionsDir()) + sep)) return false;
|
|
924
|
+
if (!existsSync(file)) return false;
|
|
925
|
+
rmSync(file);
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
920
928
|
function listSessions() {
|
|
921
929
|
ensureDirs();
|
|
922
930
|
const files = readdirSync(sessionsDir()).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -925,6 +933,8 @@ function listSessions() {
|
|
|
925
933
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
926
934
|
const records = readSession(sessionId);
|
|
927
935
|
if (records.length === 0) continue;
|
|
936
|
+
const setup = records.find((r) => r.docType === "SetupRequest");
|
|
937
|
+
const operation = setup ? /<PunchOutSetupRequest[^>]*\boperation="([^"]+)"/.exec(setup.body)?.[1] : void 0;
|
|
928
938
|
summaries.push({
|
|
929
939
|
sessionId: records[0].sessionId ?? sessionId,
|
|
930
940
|
connectionId: records[0].connectionId,
|
|
@@ -932,7 +942,8 @@ function listSessions() {
|
|
|
932
942
|
firstTs: records[0].ts,
|
|
933
943
|
lastTs: records[records.length - 1].ts,
|
|
934
944
|
docTypes: [...new Set(records.map((r) => r.docType))],
|
|
935
|
-
hasErrors: records.some((r) => r.validation && !r.validation.ok)
|
|
945
|
+
hasErrors: records.some((r) => r.validation && !r.validation.ok),
|
|
946
|
+
operation
|
|
936
947
|
});
|
|
937
948
|
}
|
|
938
949
|
return summaries.sort((a, b) => (b.lastTs ?? "").localeCompare(a.lastTs ?? ""));
|
|
@@ -944,7 +955,7 @@ function readAllRecent(limit = 200) {
|
|
|
944
955
|
// src/server/store/attachments.ts
|
|
945
956
|
import { createHash } from "crypto";
|
|
946
957
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
947
|
-
import { resolve as
|
|
958
|
+
import { resolve as resolve3 } from "path";
|
|
948
959
|
|
|
949
960
|
// src/server/cxml/multipart.ts
|
|
950
961
|
import { nanoid as nanoid3 } from "nanoid";
|
|
@@ -1082,7 +1093,7 @@ function findHeaderBodySplit(buf) {
|
|
|
1082
1093
|
function saveAttachment(data, meta) {
|
|
1083
1094
|
ensureDirs();
|
|
1084
1095
|
const hash = createHash("sha256").update(data).digest("hex");
|
|
1085
|
-
const path =
|
|
1096
|
+
const path = resolve3(attachmentsDir(), hash);
|
|
1086
1097
|
if (!existsSync2(path)) writeFileSync(path, data, { mode: 384 });
|
|
1087
1098
|
return {
|
|
1088
1099
|
contentId: normalizeContentId(meta.contentId),
|
|
@@ -1096,7 +1107,7 @@ function saveAttachment(data, meta) {
|
|
|
1096
1107
|
function readAttachment(hash) {
|
|
1097
1108
|
const safe = hash.replace(/[^a-f0-9]/gi, "");
|
|
1098
1109
|
if (!safe) return void 0;
|
|
1099
|
-
const path =
|
|
1110
|
+
const path = resolve3(attachmentsDir(), safe);
|
|
1100
1111
|
if (!existsSync2(path)) return void 0;
|
|
1101
1112
|
return readFileSync2(path);
|
|
1102
1113
|
}
|
|
@@ -1116,6 +1127,10 @@ function rememberSessionConnection(sessionId, connectionId) {
|
|
|
1116
1127
|
function connectionForSession(sessionId) {
|
|
1117
1128
|
return connectionBySession.get(sessionId);
|
|
1118
1129
|
}
|
|
1130
|
+
function forgetSession(sessionId) {
|
|
1131
|
+
carts.delete(sessionId);
|
|
1132
|
+
connectionBySession.delete(sessionId);
|
|
1133
|
+
}
|
|
1119
1134
|
|
|
1120
1135
|
// src/server/http.ts
|
|
1121
1136
|
function assertSafeOutboundUrl(url) {
|
|
@@ -1229,6 +1244,22 @@ function extrinsicBlock(items, indent) {
|
|
|
1229
1244
|
if (!items || items.length === 0) return "";
|
|
1230
1245
|
return "\n" + items.map((e) => `${indent}<Extrinsic name="${escapeXml(e.name)}">${escapeXml(e.value)}</Extrinsic>`).join("\n");
|
|
1231
1246
|
}
|
|
1247
|
+
function setupItemOut(it, idx) {
|
|
1248
|
+
return ` <ItemOut quantity="${escapeXml(it.quantity)}" lineNumber="${idx}">
|
|
1249
|
+
<ItemID>
|
|
1250
|
+
<SupplierPartID>${escapeXml(it.supplierPartId ?? "")}</SupplierPartID>${it.supplierPartAuxiliaryId ? `
|
|
1251
|
+
<SupplierPartAuxiliaryID>${escapeXml(it.supplierPartAuxiliaryId)}</SupplierPartAuxiliaryID>` : ""}
|
|
1252
|
+
</ItemID>
|
|
1253
|
+
<ItemDetail>
|
|
1254
|
+
<UnitPrice>
|
|
1255
|
+
<Money currency="${escapeXml(it.currency ?? "USD")}">${escapeXml(it.unitPriceAmount ?? 0)}</Money>
|
|
1256
|
+
</UnitPrice>
|
|
1257
|
+
<Description xml:lang="en">${escapeXml(it.description ?? "")}</Description>
|
|
1258
|
+
<UnitOfMeasure>${escapeXml(it.uom ?? "EA")}</UnitOfMeasure>
|
|
1259
|
+
${classificationBlock(it, " ")}
|
|
1260
|
+
</ItemDetail>
|
|
1261
|
+
</ItemOut>`;
|
|
1262
|
+
}
|
|
1232
1263
|
function buildSetupRequest(o) {
|
|
1233
1264
|
const lang = o.lang ?? "en-US";
|
|
1234
1265
|
const deployment = o.deploymentMode ? ` deploymentMode="${escapeXml(o.deploymentMode)}"` : "";
|
|
@@ -1237,6 +1268,8 @@ function buildSetupRequest(o) {
|
|
|
1237
1268
|
${addressBlock("ShipTo", o.shipTo, mode)}` : "";
|
|
1238
1269
|
const contact2 = o.contact ? `
|
|
1239
1270
|
${contactBlock(o.contact, mode, " ")}` : "";
|
|
1271
|
+
const items = o.items && o.items.length > 0 ? `
|
|
1272
|
+
${o.items.map((it, i) => setupItemOut(it, i + 1)).join("\n")}` : "";
|
|
1240
1273
|
const inner = `${header({
|
|
1241
1274
|
from: o.from,
|
|
1242
1275
|
to: o.to,
|
|
@@ -1249,7 +1282,7 @@ ${contactBlock(o.contact, mode, " ")}` : "";
|
|
|
1249
1282
|
<BuyerCookie>${escapeXml(o.buyerCookie)}</BuyerCookie>
|
|
1250
1283
|
<BrowserFormPost>
|
|
1251
1284
|
<URL>${escapeXml(o.browserFormPostUrl)}</URL>
|
|
1252
|
-
</BrowserFormPost>${extrinsicBlock(o.extrinsics, " ")}${shipTo}${contact2}
|
|
1285
|
+
</BrowserFormPost>${extrinsicBlock(o.extrinsics, " ")}${shipTo}${contact2}${items}
|
|
1253
1286
|
</PunchOutSetupRequest>
|
|
1254
1287
|
</Request>`;
|
|
1255
1288
|
return envelope(o.payloadId, o.timestamp, lang, inner, o.dtdVersion);
|
|
@@ -1971,12 +2004,22 @@ function validateDocument(raw, ctx = {}) {
|
|
|
1971
2004
|
const docType = ctx.forceDocType ?? getDocType(doc);
|
|
1972
2005
|
checkGeneral(doc, ctx, issues, docType);
|
|
1973
2006
|
switch (docType) {
|
|
1974
|
-
case "SetupRequest":
|
|
2007
|
+
case "SetupRequest": {
|
|
1975
2008
|
checkSharedSecret(doc, ctx, issues);
|
|
1976
|
-
|
|
2009
|
+
const setup = root(doc)?.Request?.PunchOutSetupRequest;
|
|
2010
|
+
if (!setup?.BrowserFormPost?.URL) {
|
|
1977
2011
|
issues.warn("missing-browserformpost", "PunchOutSetupRequest/BrowserFormPost/URL is missing", "cXML/.../PunchOutSetupRequest/BrowserFormPost/URL");
|
|
1978
2012
|
}
|
|
2013
|
+
const op = attr(setup, "operation") ?? "create";
|
|
2014
|
+
const hasItems = asArray(setup?.ItemOut).length > 0;
|
|
2015
|
+
if ((op === "edit" || op === "inspect") && !hasItems) {
|
|
2016
|
+
issues.warn("setup-missing-items", `operation="${op}" usually carries the prior item(s) as ItemOut, but none are present`, "cXML/.../PunchOutSetupRequest");
|
|
2017
|
+
}
|
|
2018
|
+
if (op === "create" && hasItems) {
|
|
2019
|
+
issues.warn("setup-unexpected-items", 'operation="create" starts an empty cart, but ItemOut elements are present', "cXML/.../PunchOutSetupRequest");
|
|
2020
|
+
}
|
|
1979
2021
|
break;
|
|
2022
|
+
}
|
|
1980
2023
|
case "SetupResponse":
|
|
1981
2024
|
checkSetupResponse(doc, issues);
|
|
1982
2025
|
break;
|
|
@@ -1998,6 +2041,8 @@ function validateDocument(raw, ctx = {}) {
|
|
|
1998
2041
|
}
|
|
1999
2042
|
|
|
2000
2043
|
// src/server/routes/flow.ts
|
|
2044
|
+
var coerceOperation = (v) => v === "create" || v === "edit" || v === "inspect" ? v : void 0;
|
|
2045
|
+
var setupItems = (body) => Array.isArray(body?.items) && body.items.length > 0 ? body.items : void 0;
|
|
2001
2046
|
var flowRoute = new Hono5();
|
|
2002
2047
|
function host() {
|
|
2003
2048
|
try {
|
|
@@ -2049,11 +2094,12 @@ function resolveVirtualBuyer(id) {
|
|
|
2049
2094
|
if (r.connection.mode !== "virtual-buyer") return { error: "connection is not in virtual-buyer mode" };
|
|
2050
2095
|
return { ctx: buyerContext(r) };
|
|
2051
2096
|
}
|
|
2052
|
-
flowRoute.
|
|
2097
|
+
flowRoute.post("/:id/setup/preview", async (c) => {
|
|
2053
2098
|
const r = resolveVirtualBuyer(c.req.param("id"));
|
|
2054
2099
|
if ("error" in r) return c.json({ error: r.error }, 400);
|
|
2055
2100
|
const { ctx } = r;
|
|
2056
|
-
const
|
|
2101
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2102
|
+
const buyerCookie = body.buyerCookie || `pos-${nanoid5(16)}`;
|
|
2057
2103
|
const xml = buildSetupRequest({
|
|
2058
2104
|
from: ctx.from,
|
|
2059
2105
|
to: ctx.to,
|
|
@@ -2064,11 +2110,12 @@ flowRoute.get("/:id/setup/preview", (c) => {
|
|
|
2064
2110
|
payloadId: makePayloadId(host(), (/* @__PURE__ */ new Date()).toISOString()),
|
|
2065
2111
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2066
2112
|
deploymentMode: ctx.deploymentMode,
|
|
2067
|
-
operation: ctx.eff.setupOperation,
|
|
2113
|
+
operation: coerceOperation(body.operation) ?? ctx.eff.setupOperation,
|
|
2068
2114
|
dtdVersion: dtdVersionFor(ctx.eff, "SetupRequest"),
|
|
2069
2115
|
userAgent: ctx.eff.userAgent,
|
|
2070
2116
|
extrinsics: setupExtrinsics(ctx, buyerCookie),
|
|
2071
|
-
...setupAddresses(ctx)
|
|
2117
|
+
...setupAddresses(ctx),
|
|
2118
|
+
items: setupItems(body)
|
|
2072
2119
|
});
|
|
2073
2120
|
return c.json({ buyerCookie, xml, browserFormPostUrl: browserFormPostUrl() });
|
|
2074
2121
|
});
|
|
@@ -2088,11 +2135,12 @@ flowRoute.post("/:id/setup", async (c) => {
|
|
|
2088
2135
|
payloadId: makePayloadId(host(), (/* @__PURE__ */ new Date()).toISOString()),
|
|
2089
2136
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2090
2137
|
deploymentMode: ctx.deploymentMode,
|
|
2091
|
-
operation: ctx.eff.setupOperation,
|
|
2138
|
+
operation: coerceOperation(body.operation) ?? ctx.eff.setupOperation,
|
|
2092
2139
|
dtdVersion: dtdVersionFor(ctx.eff, "SetupRequest"),
|
|
2093
2140
|
userAgent: ctx.eff.userAgent,
|
|
2094
2141
|
extrinsics: setupExtrinsics(ctx, buyerCookie),
|
|
2095
|
-
...setupAddresses(ctx)
|
|
2142
|
+
...setupAddresses(ctx),
|
|
2143
|
+
items: setupItems(body)
|
|
2096
2144
|
});
|
|
2097
2145
|
rememberSessionConnection(buyerCookie, ctx.connectionId);
|
|
2098
2146
|
const reqValidation = validateDocument(xml, { expected: ctx.expected, forceDocType: "SetupRequest" });
|
|
@@ -2402,8 +2450,37 @@ dataRoute.get(
|
|
|
2402
2450
|
"/runtime",
|
|
2403
2451
|
(c) => c.json({ publicUrl: getPublicUrl(), callbackUrl: `${getPublicUrl()}/punchout/return`, version: getVersion() })
|
|
2404
2452
|
);
|
|
2405
|
-
dataRoute.get("/sessions", (c) =>
|
|
2453
|
+
dataRoute.get("/sessions", (c) => {
|
|
2454
|
+
const enriched = listSessions().map((s) => {
|
|
2455
|
+
const conn = s.connectionId ? getConnection(s.connectionId) : void 0;
|
|
2456
|
+
if (conn) {
|
|
2457
|
+
return {
|
|
2458
|
+
...s,
|
|
2459
|
+
connectionName: conn.name,
|
|
2460
|
+
mode: conn.mode,
|
|
2461
|
+
buyerName: getBuyer(conn.buyerId)?.name,
|
|
2462
|
+
supplierName: getSupplier(conn.supplierId)?.name,
|
|
2463
|
+
inbound: false
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
const supplier = s.connectionId ? getSupplier(s.connectionId) : void 0;
|
|
2467
|
+
return {
|
|
2468
|
+
...s,
|
|
2469
|
+
supplierName: supplier?.name,
|
|
2470
|
+
mode: supplier ? "virtual-supplier" : void 0,
|
|
2471
|
+
inbound: !!supplier
|
|
2472
|
+
// an external buyer hit our /sim
|
|
2473
|
+
};
|
|
2474
|
+
});
|
|
2475
|
+
return c.json(enriched);
|
|
2476
|
+
});
|
|
2406
2477
|
dataRoute.get("/sessions/:id", (c) => c.json(readSession(c.req.param("id"))));
|
|
2478
|
+
dataRoute.delete("/sessions/:id", (c) => {
|
|
2479
|
+
const id = c.req.param("id");
|
|
2480
|
+
const removed = deleteSession(id);
|
|
2481
|
+
forgetSession(id);
|
|
2482
|
+
return removed ? c.json({ ok: true }) : c.json({ error: "not found" }, 404);
|
|
2483
|
+
});
|
|
2407
2484
|
dataRoute.get("/sessions/:sessionId/records/:recordId/raw", (c) => {
|
|
2408
2485
|
const record = readSession(c.req.param("sessionId")).find((r) => r.id === c.req.param("recordId"));
|
|
2409
2486
|
if (!record) return c.json({ error: "not found" }, 404);
|
|
@@ -2580,7 +2657,10 @@ simRoute.post("/:id/checkout", async (c) => {
|
|
|
2580
2657
|
const supplier = getSupplier(c.req.param("id"));
|
|
2581
2658
|
if (!supplier) return c.text("supplier not found", 404);
|
|
2582
2659
|
const form = await c.req.parseBody();
|
|
2583
|
-
const cookie =
|
|
2660
|
+
const cookie = typeof form.cookie === "string" ? form.cookie.trim() : "";
|
|
2661
|
+
if (!cookie) {
|
|
2662
|
+
return c.text("missing BuyerCookie \u2014 open the catalog from a SetupResponse StartPage", 400);
|
|
2663
|
+
}
|
|
2584
2664
|
const formpost = safeHttpUrl(String(form.formpost ?? ""));
|
|
2585
2665
|
const buyerCred = { domain: String(form.bd ?? ""), identity: String(form.bi ?? "") };
|
|
2586
2666
|
const catalog = catalogOf(supplier);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as et}from"./index-
|
|
1
|
+
import{m as et}from"./index-ByIxIbJu.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as f}from"./index-
|
|
1
|
+
import{m as f}from"./index-ByIxIbJu.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as l}from"./index-
|
|
1
|
+
import{m as l}from"./index-ByIxIbJu.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as s}from"./index-
|
|
1
|
+
import{m as s}from"./index-ByIxIbJu.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as lt}from"./index-
|
|
1
|
+
import{m as lt}from"./index-ByIxIbJu.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|