punchout-simulator 0.2.0 → 0.3.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 +86 -14
- package/dist/server/cli.js +546 -94
- package/dist/web/assets/{cssMode-B-got2hv.js → cssMode-uoWbxmRA.js} +1 -1
- package/dist/web/assets/{freemarker2-RnkeBrE4.js → freemarker2-DNR9Witk.js} +1 -1
- package/dist/web/assets/{handlebars-54KQc4Ip.js → handlebars-Cz4BZbpY.js} +1 -1
- package/dist/web/assets/{html-BHfOj4V7.js → html-B08afy-k.js} +1 -1
- package/dist/web/assets/{htmlMode-Chd2dG7N.js → htmlMode-DwllrnDj.js} +1 -1
- package/dist/web/assets/{index-CdJNNMRn.js → index-BzgSetIt.js} +198 -198
- package/dist/web/assets/{index-9LlIENcD.css → index-DLIyRG88.css} +1 -1
- package/dist/web/assets/{javascript-DfxvokuB.js → javascript-CSO3Lh8M.js} +1 -1
- package/dist/web/assets/{jsonMode-VBut9FUK.js → jsonMode-BQc0D6l3.js} +1 -1
- package/dist/web/assets/{liquid-BnObGKeE.js → liquid-D_Cq8kS0.js} +1 -1
- package/dist/web/assets/{mdx-DugOiDtO.js → mdx-9UNHMbIw.js} +1 -1
- package/dist/web/assets/{python-BctDjJVU.js → python-BziTtpp-.js} +1 -1
- package/dist/web/assets/{razor-wtD6sxeJ.js → razor-CqSSZBI0.js} +1 -1
- package/dist/web/assets/{tsMode-DcIWDCPt.js → tsMode-BXILR9lc.js} +1 -1
- package/dist/web/assets/{typescript-amfHwAlg.js → typescript-lG76zlZP.js} +1 -1
- package/dist/web/assets/{xml-CRuxB8WJ.js → xml-D1mrwtCd.js} +1 -1
- package/dist/web/assets/{yaml-D_zXW3Py.js → yaml-DWKj4dJQ.js} +1 -1
- package/dist/web/favicon.svg +8 -0
- package/dist/web/index.html +12 -2
- package/package.json +1 -1
- package/dist/server/cli.js.map +0 -1
package/README.md
CHANGED
|
@@ -30,12 +30,15 @@ That's it — no install, no build. It boots a local server, opens your browser,
|
|
|
30
30
|
and seeds a **built-in demo**: a virtual buyer wired to a built-in mock supplier,
|
|
31
31
|
so you can run the entire roundtrip immediately:
|
|
32
32
|
|
|
33
|
-
1. Open the **Demo Buyer →
|
|
33
|
+
1. Open the **Demo Buyer → Demo Supplier** connection.
|
|
34
34
|
2. **Send SetupRequest** → the mock supplier replies with a StartPage.
|
|
35
35
|
3. **Open the catalog**, set quantities, **return the cart** — the punchback
|
|
36
36
|
lands back in the app live.
|
|
37
|
-
4. **
|
|
38
|
-
|
|
37
|
+
4. **Build the OrderRequest**, edit the cXML if you want (tweak `<Comments>`,
|
|
38
|
+
addresses, attachment refs), optionally attach files at the **order or item
|
|
39
|
+
level**, optionally flip on the **dangling-`cid` test**, then **send it** and
|
|
40
|
+
inspect the supplier's response.
|
|
41
|
+
5. If validation fails, **edit and re-send** — the flow session and cart persist.
|
|
39
42
|
|
|
40
43
|
Every document — inbound and outbound — is validated and logged.
|
|
41
44
|
|
|
@@ -46,8 +49,10 @@ npm i -g punchout-simulator && punchout-simulator # persistent install
|
|
|
46
49
|
```
|
|
47
50
|
|
|
48
51
|
```bash
|
|
49
|
-
# Docker
|
|
50
|
-
|
|
52
|
+
# Docker (binds 0.0.0.0 inside the container ⇒ counts as "exposed" ⇒ /api needs a
|
|
53
|
+
# token). Set your own, or read the auto-generated ?token= URL from the logs:
|
|
54
|
+
docker run -p 127.0.0.1:8080:8080 -e POS_TOKEN=mysecret -v "$PWD/data:/data" punchout-simulator
|
|
55
|
+
# then open http://localhost:8080/?token=mysecret
|
|
51
56
|
```
|
|
52
57
|
|
|
53
58
|
### CLI options
|
|
@@ -57,12 +62,25 @@ docker run -p 8080:8080 -v "$PWD/data:/data" punchout-simulator
|
|
|
57
62
|
-d, --data-dir <path> Where to store config + logs (default ./data)
|
|
58
63
|
--public-url <url> Externally reachable base URL (default http://localhost:<port>)
|
|
59
64
|
Set this when fronting the tool with ngrok/cloudflared.
|
|
65
|
+
--host <addr> Bind address (default 127.0.0.1; use 0.0.0.0 to expose on the LAN)
|
|
66
|
+
--token <secret> Require this token on /api (auto-generated when exposed; or set POS_TOKEN)
|
|
60
67
|
--no-open Do not open a browser on start
|
|
61
|
-
--no-seed Do not seed the built-in demo
|
|
68
|
+
--no-seed Do not seed the built-in demo buyer/supplier/connection on first run
|
|
62
69
|
--dev Dev mode (do not serve the SPA, do not open a browser)
|
|
63
70
|
-h, --help Show help
|
|
64
71
|
```
|
|
65
72
|
|
|
73
|
+
### Exposure & the admin API
|
|
74
|
+
|
|
75
|
+
The control plane (`/api/*` — config CRUD, logs, the live stream) is **bound to
|
|
76
|
+
`127.0.0.1` by default** and is unauthenticated only for that loopback case. The
|
|
77
|
+
moment the tool is **exposed** — a non-loopback `--public-url` (ngrok/cloudflared)
|
|
78
|
+
or `--host 0.0.0.0` — it **requires a token** on `/api/*`: one is auto-generated
|
|
79
|
+
(or pass `--token`/`POS_TOKEN`) and printed as a `…/?token=…` URL to open the UI
|
|
80
|
+
with. The **inbound buyer surface stays open** (`/sim/*`, `/punchout/return`) so a
|
|
81
|
+
real buyer system can still reach Mode B. Shared secrets are **write-only** over
|
|
82
|
+
the API (masked on read) and **redacted from all logs**.
|
|
83
|
+
|
|
66
84
|
---
|
|
67
85
|
|
|
68
86
|
## What it checks (validation / linting)
|
|
@@ -73,7 +91,8 @@ document in both directions, and results are surfaced per message in the UI
|
|
|
73
91
|
emit correct cXML?"* linter as a transport driver.
|
|
74
92
|
|
|
75
93
|
General checks: well-formedness, `payloadID`/`timestamp` presence, and
|
|
76
|
-
`From`/`To`/`Sender`
|
|
94
|
+
`From`/`To`/`Sender` credentials + `SharedSecret` consistency with the
|
|
95
|
+
connection's buyer/supplier identities.
|
|
77
96
|
|
|
78
97
|
Document-specific:
|
|
79
98
|
|
|
@@ -97,6 +116,30 @@ receiver detects the missing attachment. `<Comments>` (and therefore
|
|
|
97
116
|
`<Attachment>`) is scanned at **both** levels: `OrderRequestHeader` and each
|
|
98
117
|
`ItemOut`.
|
|
99
118
|
|
|
119
|
+
### Editing the OrderRequest, attachments & retry
|
|
120
|
+
|
|
121
|
+
- The OrderRequest is built from the returned cart into an editable Monaco view.
|
|
122
|
+
**Edit it before sending** — `<Comments>`, addresses, attachment references,
|
|
123
|
+
anything; the exact document you see is what gets sent.
|
|
124
|
+
- **Attachments are scoped per item or per order**, so the `cid` reference lands
|
|
125
|
+
in the right `ItemOut/Comments` or `OrderRequestHeader/Comments`.
|
|
126
|
+
- **Transfer encoding is a per-connection setting** (`attachmentEncoding`):
|
|
127
|
+
`binary` writes the raw bytes into each MIME part (valid over HTTP and more
|
|
128
|
+
compact), while `base64` is the `Content-Transfer-Encoding` most real
|
|
129
|
+
Ariba/Coupa receivers expect. Switch it in the connection editor to exercise
|
|
130
|
+
either ingestion path; the built-in mock supplier (Mode B) decodes whichever
|
|
131
|
+
it receives. Defaults to `binary`.
|
|
132
|
+
- The flow is a **persistent per-connection session**: switching between the
|
|
133
|
+
Flow and Settings tabs (or connections) keeps your in-progress order, and you
|
|
134
|
+
can **edit and re-send** after a supplier rejection. "New session" starts a
|
|
135
|
+
fresh `BuyerCookie`.
|
|
136
|
+
- Every logged message has a **cXML / Raw toggle**. "Raw" shows the full wire
|
|
137
|
+
message — headers plus, for an order with attachments, the reassembled
|
|
138
|
+
`multipart/related` envelope (boundaries, part headers, `Content-ID`, the cXML
|
|
139
|
+
and each attachment part) — with a one-click **Copy**. The raw body is
|
|
140
|
+
reconstructed on demand from the document + the attachments on disk, so the
|
|
141
|
+
log stays free of inlined base64.
|
|
142
|
+
|
|
100
143
|
---
|
|
101
144
|
|
|
102
145
|
## Network reachability
|
|
@@ -124,7 +167,7 @@ backend, so there is no CORS between them).
|
|
|
124
167
|
| XML parsing | `fast-xml-parser` |
|
|
125
168
|
| cXML building | template literals (full control over shape/ordering/`xml:lang`) |
|
|
126
169
|
| multipart/related | hand-assembled (`Buffer` + boundary) |
|
|
127
|
-
| Storage | `lowdb` (`config.json`) for buyers/suppliers/connections · append-only JSONL per session for logs · separate files for attachments |
|
|
170
|
+
| Storage | `lowdb` (`config.json`) for buyers/suppliers/connections/profiles · append-only JSONL per session for logs · separate files for attachments |
|
|
128
171
|
|
|
129
172
|
```
|
|
130
173
|
src/
|
|
@@ -135,23 +178,52 @@ src/
|
|
|
135
178
|
|
|
136
179
|
### Data model
|
|
137
180
|
|
|
138
|
-
The config is normalized into
|
|
181
|
+
The config is normalized into four entities:
|
|
139
182
|
|
|
140
|
-
- **Buyer** — a reusable party holding its own cXML identity (the `From` credential).
|
|
183
|
+
- **Buyer** — a reusable party holding its own cXML identity (the `From` credential) and an optional **platform profile** reference (see below).
|
|
141
184
|
- **Supplier** — a reusable party holding its cXML identity (`To`) plus its **endpoints** (PunchOut URL, Order URL) and an optional mock catalog. Endpoints are intrinsic to the supplier — defined once, not per relationship.
|
|
142
|
-
- **Connection** — the edge pairing one Buyer with one Supplier. It holds only what is specific to that pair: which side the tool simulates (`mode`), the `sharedSecret`, an optional per-pair Sender identity override (defaults to the buyer's identity), `authStyle`, and `
|
|
185
|
+
- **Connection** — the edge pairing one Buyer with one Supplier. It holds only what is specific to that pair: which side the tool simulates (`mode`), the `sharedSecret`, an optional per-pair Sender identity override (defaults to the buyer's identity), `authStyle`, `deploymentMode`, and `attachmentEncoding`.
|
|
186
|
+
- **Profile** — a reusable **procurement-platform profile** (Ariba/Coupa/Jaggaer/…) referenced by a Buyer. See below.
|
|
143
187
|
|
|
144
188
|
At send time: `From` = buyer identity, `To` = supplier identity, `Sender` = the connection's override (or the buyer), and the request targets the supplier's endpoints. The mock-supplier endpoints are keyed by supplier id (`/sim/<supplierId>/…`).
|
|
145
189
|
|
|
190
|
+
### Simulating different procurement platforms (Buyer profiles)
|
|
191
|
+
|
|
192
|
+
Real procurement systems emit cXML differently. A **Profile** captures a platform's
|
|
193
|
+
emission behavior and is assigned to a Buyer, so you can drive a supplier as if it were
|
|
194
|
+
Ariba, Coupa, Jaggaer, Oracle, SAP, or Workday:
|
|
195
|
+
|
|
196
|
+
| Profile field | Effect on the documents the buyer emits |
|
|
197
|
+
|---|---|
|
|
198
|
+
| `dtdVersions` | The `<!DOCTYPE … cXML/<version>/cXML.dtd>` **per document type** (e.g. Coupa: SetupRequest `1.2.014`, PunchOutOrderMessage `1.2.023`). |
|
|
199
|
+
| `userAgent` | The `<UserAgent>` in the Sender. |
|
|
200
|
+
| `setupOperation` | `PunchOutSetupRequest@operation` (`create`/`edit`/`inspect`). |
|
|
201
|
+
| `attachmentEncoding` | Default `Content-Transfer-Encoding` for OrderRequest attachments (`binary`/`base64`). |
|
|
202
|
+
| `cartReturnTransport` | How the punchback is returned in Mode B: `cxml-urlencoded`, `cxml-base64`, or `raw`. |
|
|
203
|
+
| `extrinsics` | `<Extrinsic>` templates injected into the setup/order documents (values may use `${buyerCookie}` / `${orderId}`). |
|
|
204
|
+
|
|
205
|
+
Built-in presets (Ariba, Coupa, Jaggaer, Oracle, SAP, Workday, Generic) ship out of the box
|
|
206
|
+
and can be **loaded into the editor** as a starting point, then customized. A Profile holds
|
|
207
|
+
the *defaults*; a Connection's own `attachmentEncoding` overrides it per pair. A Buyer with no
|
|
208
|
+
profile uses the **Generic** defaults (`1.2.045` / `punchout-simulator` / binary /
|
|
209
|
+
cxml-urlencoded) — i.e. the tool's original behavior.
|
|
210
|
+
|
|
211
|
+
> Authentication is **SharedSecret** only. (MAC / `CredentialMac` — used by network-routed
|
|
212
|
+
> Ariba/SAP flows — is intentionally out of scope for now.)
|
|
213
|
+
>
|
|
214
|
+
> SAP SRM in reality speaks **OCI** (form parameters), which this cXML-only tool cannot emit;
|
|
215
|
+
> the SAP profile models the cXML/Business-Network side (base64 attachments + cart return).
|
|
216
|
+
|
|
146
217
|
### Endpoints
|
|
147
218
|
|
|
148
219
|
- `*/api/buyers`, `*/api/suppliers` — CRUD for the reusable parties
|
|
220
|
+
- `*/api/profiles` — CRUD for procurement-platform profiles · `GET /api/profile-presets` — the built-in preset library
|
|
149
221
|
- `*/api/connections` — CRUD for connection edges (reference a buyer + supplier)
|
|
150
|
-
- `
|
|
151
|
-
- `POST /api/connections/:id/order` — send the OrderRequest (multipart if attachments)
|
|
222
|
+
- `GET /api/connections/:id/setup/preview` · `POST …/setup` — preview / send the SetupRequest
|
|
223
|
+
- `POST /api/connections/:id/order/preview` · `POST …/order` — preview / send the OrderRequest (multipart if attachments)
|
|
152
224
|
- `POST /punchout/return` — callback receiving the punchback auto-submit
|
|
153
225
|
- `GET /api/stream` — SSE live log
|
|
154
|
-
- `/sim/:
|
|
226
|
+
- `/sim/:supplierId/*` — Mode B mock supplier (punchout · catalog · checkout · order)
|
|
155
227
|
|
|
156
228
|
---
|
|
157
229
|
|