@stardeck-customer-apps/testing 0.4.0 → 0.5.0
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/SKILL.md +15 -0
- package/dist/index.d.mts +103 -1
- package/dist/index.d.ts +103 -1
- package/dist/index.js +238 -4
- package/dist/index.mjs +238 -4
- package/dist/next/headers-shim.js +7 -0
- package/dist/next/headers-shim.mjs +7 -0
- package/dist/setup.js +188 -4
- package/dist/setup.mjs +188 -4
- package/package.json +6 -1
package/SKILL.md
CHANGED
|
@@ -101,6 +101,15 @@ expect(app.storage.latest()?.filename).toBe("note.txt");
|
|
|
101
101
|
const integrations = createIntegrationsClient();
|
|
102
102
|
await integrations.line.push("U123", { type: "text", text: "Your order shipped" });
|
|
103
103
|
expect(app.messages.channel("line").to("U123")[0].body.text).toMatch(/shipped/i);
|
|
104
|
+
|
|
105
|
+
// Receipt print + display
|
|
106
|
+
import { createEdgeClient } from "@stardeck-customer-apps/edge-sdk/server";
|
|
107
|
+
|
|
108
|
+
const edge = createEdgeClient();
|
|
109
|
+
await edge.print({ receipt: { total: 42 }, alias: "station-1", openDrawer: true });
|
|
110
|
+
expect(app.edge.latestPrint()?.receipt.total).toBe(42);
|
|
111
|
+
await edge.showDisplay({ alias: "customer-screen", url: "https://app.example.com/kiosk" });
|
|
112
|
+
expect(app.edge.latestDisplay()?.action).toBe("show");
|
|
104
113
|
```
|
|
105
114
|
|
|
106
115
|
## API
|
|
@@ -131,6 +140,9 @@ expect(app.messages.channel("line").to("U123")[0].body.text).toMatch(/shipped/i)
|
|
|
131
140
|
- `app.storage` — uploads through storage-sdk: `.uploads`, `.latest()`, `.count`, `.clear()`.
|
|
132
141
|
- `app.messages` — outbound Slack/LINE/Facebook sends:
|
|
133
142
|
`.all()`, `.latest()`, `.to(recipient)`, `.channel("line")`, `.count`, `.clear()`.
|
|
143
|
+
- `app.edge` — edge print/display/bindings from edge-sdk: `.prints`, `.displays`,
|
|
144
|
+
`.testPrints`, `.latestPrint()`, `.latestDisplay()`, `.bindings`,
|
|
145
|
+
`.seedDevices()`, `.seedPeripherals()`, `.seedBindings()`, `.count`, `.clear()`.
|
|
134
146
|
- `app.query(sql, params?)` / `app.db` — direct database access for
|
|
135
147
|
assertions and seeding.
|
|
136
148
|
- `callRoute(handler, opts)` — invoke an App Router route handler with a real
|
|
@@ -161,6 +173,9 @@ expect(app.messages.channel("line").to("U123")[0].body.text).toMatch(/shipped/i)
|
|
|
161
173
|
in `app.storage` against the simulated `STORAGE_URL` host.
|
|
162
174
|
- `client.slack` / `client.line` / `client.facebook` send endpoints — captured
|
|
163
175
|
in `app.messages`.
|
|
176
|
+
- `createEdgeClient()` (print, pair/unpair, list devices/peripherals, show/clear
|
|
177
|
+
display, test print) — captured in `app.edge`; seed devices/peripherals for
|
|
178
|
+
pairing pickers via `app.edge.seedDevices()` / `seedPeripherals()`.
|
|
164
179
|
- `next/headers` (`headers()`/`cookies()`) inside handlers under `callRoute`.
|
|
165
180
|
|
|
166
181
|
## Rules
|
package/dist/index.d.mts
CHANGED
|
@@ -184,6 +184,106 @@ interface TestMessages {
|
|
|
184
184
|
clear(): void;
|
|
185
185
|
get count(): number;
|
|
186
186
|
}
|
|
187
|
+
interface ReceiptPayload {
|
|
188
|
+
header?: {
|
|
189
|
+
lines: string[];
|
|
190
|
+
};
|
|
191
|
+
items?: Array<{
|
|
192
|
+
name: string;
|
|
193
|
+
quantity: number;
|
|
194
|
+
unitPrice: number;
|
|
195
|
+
total: number;
|
|
196
|
+
note?: string;
|
|
197
|
+
}>;
|
|
198
|
+
subtotal?: number;
|
|
199
|
+
tax?: number;
|
|
200
|
+
discount?: number;
|
|
201
|
+
total?: number;
|
|
202
|
+
payments?: Array<{
|
|
203
|
+
method: string;
|
|
204
|
+
amount: number;
|
|
205
|
+
}>;
|
|
206
|
+
footer?: {
|
|
207
|
+
lines: string[];
|
|
208
|
+
};
|
|
209
|
+
barcode?: {
|
|
210
|
+
type: "qr" | "code128";
|
|
211
|
+
data: string;
|
|
212
|
+
};
|
|
213
|
+
rawEscPos?: string;
|
|
214
|
+
}
|
|
215
|
+
interface DeviceInfo {
|
|
216
|
+
id: string;
|
|
217
|
+
displayName: string;
|
|
218
|
+
status: "pairing_pending" | "online" | "offline" | "decommissioned";
|
|
219
|
+
isDefault: boolean;
|
|
220
|
+
lastHeartbeatAt: string | null;
|
|
221
|
+
}
|
|
222
|
+
interface PeripheralInfo {
|
|
223
|
+
id: string;
|
|
224
|
+
displayName: string | null;
|
|
225
|
+
driver: string | null;
|
|
226
|
+
transport: "usb_lp" | "serial" | "tcp" | "usb_raw" | "display";
|
|
227
|
+
connected: boolean;
|
|
228
|
+
lastSeenAt: string | null;
|
|
229
|
+
device: {
|
|
230
|
+
id: string;
|
|
231
|
+
displayName: string;
|
|
232
|
+
status: DeviceInfo["status"];
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
type BindingState = "ok" | "peripheral_missing" | "grant_revoked";
|
|
236
|
+
interface BindingInfo {
|
|
237
|
+
alias: string;
|
|
238
|
+
state: BindingState;
|
|
239
|
+
peripheral: {
|
|
240
|
+
id: string;
|
|
241
|
+
displayName: string | null;
|
|
242
|
+
driver: string | null;
|
|
243
|
+
connected: boolean;
|
|
244
|
+
} | null;
|
|
245
|
+
device: {
|
|
246
|
+
id: string;
|
|
247
|
+
displayName: string;
|
|
248
|
+
status: DeviceInfo["status"];
|
|
249
|
+
} | null;
|
|
250
|
+
updatedAt: string;
|
|
251
|
+
}
|
|
252
|
+
interface CapturedPrint {
|
|
253
|
+
jobId: string;
|
|
254
|
+
deploymentId: string;
|
|
255
|
+
alias?: string;
|
|
256
|
+
deviceId?: string;
|
|
257
|
+
peripheralId?: string;
|
|
258
|
+
receipt: ReceiptPayload;
|
|
259
|
+
openDrawer: boolean;
|
|
260
|
+
logo?: boolean;
|
|
261
|
+
copies: number;
|
|
262
|
+
}
|
|
263
|
+
interface CapturedDisplay {
|
|
264
|
+
action: "show" | "clear";
|
|
265
|
+
alias?: string;
|
|
266
|
+
peripheralId?: string;
|
|
267
|
+
url?: string;
|
|
268
|
+
}
|
|
269
|
+
interface CapturedTestPrint {
|
|
270
|
+
alias?: string;
|
|
271
|
+
deviceId?: string;
|
|
272
|
+
peripheralId?: string;
|
|
273
|
+
}
|
|
274
|
+
interface TestEdge {
|
|
275
|
+
get prints(): CapturedPrint[];
|
|
276
|
+
get displays(): CapturedDisplay[];
|
|
277
|
+
get testPrints(): CapturedTestPrint[];
|
|
278
|
+
latestPrint(): CapturedPrint | undefined;
|
|
279
|
+
latestDisplay(): CapturedDisplay | undefined;
|
|
280
|
+
get bindings(): BindingInfo[];
|
|
281
|
+
seedDevices(devices: DeviceInfo[]): void;
|
|
282
|
+
seedPeripherals(peripherals: PeripheralInfo[]): void;
|
|
283
|
+
seedBindings(bindings: BindingInfo[]): void;
|
|
284
|
+
clear(): void;
|
|
285
|
+
get count(): number;
|
|
286
|
+
}
|
|
187
287
|
interface TestAppOptions {
|
|
188
288
|
/**
|
|
189
289
|
* Path to the schema.sql snapshot generated by
|
|
@@ -219,6 +319,8 @@ interface TestApp {
|
|
|
219
319
|
storage: TestStorage;
|
|
220
320
|
/** Captured outbound messages from integrations-sdk messaging channels. */
|
|
221
321
|
messages: TestMessages;
|
|
322
|
+
/** Captured edge print/display ops and peripheral bindings from edge-sdk. */
|
|
323
|
+
edge: TestEdge;
|
|
222
324
|
/** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
|
|
223
325
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
224
326
|
/**
|
|
@@ -273,4 +375,4 @@ declare const TEST_ENV_DEFAULTS: {
|
|
|
273
375
|
/** Default location of the DDL snapshot written by `generate-types`. */
|
|
274
376
|
declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
|
|
275
377
|
|
|
276
|
-
export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedUpload, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestDirectory, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
|
|
378
|
+
export { type BindingInfo, CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedDisplay, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedPrint, type CapturedTestPrint, type CapturedUpload, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, type DeviceInfo, type PeripheralInfo, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestDirectory, type TestEdge, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
|
package/dist/index.d.ts
CHANGED
|
@@ -184,6 +184,106 @@ interface TestMessages {
|
|
|
184
184
|
clear(): void;
|
|
185
185
|
get count(): number;
|
|
186
186
|
}
|
|
187
|
+
interface ReceiptPayload {
|
|
188
|
+
header?: {
|
|
189
|
+
lines: string[];
|
|
190
|
+
};
|
|
191
|
+
items?: Array<{
|
|
192
|
+
name: string;
|
|
193
|
+
quantity: number;
|
|
194
|
+
unitPrice: number;
|
|
195
|
+
total: number;
|
|
196
|
+
note?: string;
|
|
197
|
+
}>;
|
|
198
|
+
subtotal?: number;
|
|
199
|
+
tax?: number;
|
|
200
|
+
discount?: number;
|
|
201
|
+
total?: number;
|
|
202
|
+
payments?: Array<{
|
|
203
|
+
method: string;
|
|
204
|
+
amount: number;
|
|
205
|
+
}>;
|
|
206
|
+
footer?: {
|
|
207
|
+
lines: string[];
|
|
208
|
+
};
|
|
209
|
+
barcode?: {
|
|
210
|
+
type: "qr" | "code128";
|
|
211
|
+
data: string;
|
|
212
|
+
};
|
|
213
|
+
rawEscPos?: string;
|
|
214
|
+
}
|
|
215
|
+
interface DeviceInfo {
|
|
216
|
+
id: string;
|
|
217
|
+
displayName: string;
|
|
218
|
+
status: "pairing_pending" | "online" | "offline" | "decommissioned";
|
|
219
|
+
isDefault: boolean;
|
|
220
|
+
lastHeartbeatAt: string | null;
|
|
221
|
+
}
|
|
222
|
+
interface PeripheralInfo {
|
|
223
|
+
id: string;
|
|
224
|
+
displayName: string | null;
|
|
225
|
+
driver: string | null;
|
|
226
|
+
transport: "usb_lp" | "serial" | "tcp" | "usb_raw" | "display";
|
|
227
|
+
connected: boolean;
|
|
228
|
+
lastSeenAt: string | null;
|
|
229
|
+
device: {
|
|
230
|
+
id: string;
|
|
231
|
+
displayName: string;
|
|
232
|
+
status: DeviceInfo["status"];
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
type BindingState = "ok" | "peripheral_missing" | "grant_revoked";
|
|
236
|
+
interface BindingInfo {
|
|
237
|
+
alias: string;
|
|
238
|
+
state: BindingState;
|
|
239
|
+
peripheral: {
|
|
240
|
+
id: string;
|
|
241
|
+
displayName: string | null;
|
|
242
|
+
driver: string | null;
|
|
243
|
+
connected: boolean;
|
|
244
|
+
} | null;
|
|
245
|
+
device: {
|
|
246
|
+
id: string;
|
|
247
|
+
displayName: string;
|
|
248
|
+
status: DeviceInfo["status"];
|
|
249
|
+
} | null;
|
|
250
|
+
updatedAt: string;
|
|
251
|
+
}
|
|
252
|
+
interface CapturedPrint {
|
|
253
|
+
jobId: string;
|
|
254
|
+
deploymentId: string;
|
|
255
|
+
alias?: string;
|
|
256
|
+
deviceId?: string;
|
|
257
|
+
peripheralId?: string;
|
|
258
|
+
receipt: ReceiptPayload;
|
|
259
|
+
openDrawer: boolean;
|
|
260
|
+
logo?: boolean;
|
|
261
|
+
copies: number;
|
|
262
|
+
}
|
|
263
|
+
interface CapturedDisplay {
|
|
264
|
+
action: "show" | "clear";
|
|
265
|
+
alias?: string;
|
|
266
|
+
peripheralId?: string;
|
|
267
|
+
url?: string;
|
|
268
|
+
}
|
|
269
|
+
interface CapturedTestPrint {
|
|
270
|
+
alias?: string;
|
|
271
|
+
deviceId?: string;
|
|
272
|
+
peripheralId?: string;
|
|
273
|
+
}
|
|
274
|
+
interface TestEdge {
|
|
275
|
+
get prints(): CapturedPrint[];
|
|
276
|
+
get displays(): CapturedDisplay[];
|
|
277
|
+
get testPrints(): CapturedTestPrint[];
|
|
278
|
+
latestPrint(): CapturedPrint | undefined;
|
|
279
|
+
latestDisplay(): CapturedDisplay | undefined;
|
|
280
|
+
get bindings(): BindingInfo[];
|
|
281
|
+
seedDevices(devices: DeviceInfo[]): void;
|
|
282
|
+
seedPeripherals(peripherals: PeripheralInfo[]): void;
|
|
283
|
+
seedBindings(bindings: BindingInfo[]): void;
|
|
284
|
+
clear(): void;
|
|
285
|
+
get count(): number;
|
|
286
|
+
}
|
|
187
287
|
interface TestAppOptions {
|
|
188
288
|
/**
|
|
189
289
|
* Path to the schema.sql snapshot generated by
|
|
@@ -219,6 +319,8 @@ interface TestApp {
|
|
|
219
319
|
storage: TestStorage;
|
|
220
320
|
/** Captured outbound messages from integrations-sdk messaging channels. */
|
|
221
321
|
messages: TestMessages;
|
|
322
|
+
/** Captured edge print/display ops and peripheral bindings from edge-sdk. */
|
|
323
|
+
edge: TestEdge;
|
|
222
324
|
/** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
|
|
223
325
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
224
326
|
/**
|
|
@@ -273,4 +375,4 @@ declare const TEST_ENV_DEFAULTS: {
|
|
|
273
375
|
/** Default location of the DDL snapshot written by `generate-types`. */
|
|
274
376
|
declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
|
|
275
377
|
|
|
276
|
-
export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedUpload, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestDirectory, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
|
|
378
|
+
export { type BindingInfo, CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedDisplay, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedPrint, type CapturedTestPrint, type CapturedUpload, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, type DeviceInfo, type PeripheralInfo, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestDirectory, type TestEdge, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
|
package/dist/index.js
CHANGED
|
@@ -110,6 +110,13 @@ var state = globalSingleton("state", () => ({
|
|
|
110
110
|
storageFiles: /* @__PURE__ */ new Map(),
|
|
111
111
|
presignPending: /* @__PURE__ */ new Map(),
|
|
112
112
|
messages: [],
|
|
113
|
+
edgePrints: [],
|
|
114
|
+
edgeDisplays: [],
|
|
115
|
+
edgeTestPrints: [],
|
|
116
|
+
edgePrintCounter: 0,
|
|
117
|
+
edgeDevices: [],
|
|
118
|
+
edgePeripherals: [],
|
|
119
|
+
edgeBindings: /* @__PURE__ */ new Map(),
|
|
113
120
|
allowNetwork: false
|
|
114
121
|
}));
|
|
115
122
|
function requireDb() {
|
|
@@ -172,8 +179,8 @@ function verifyDeploymentAuthHeader(secret, header) {
|
|
|
172
179
|
return null;
|
|
173
180
|
}
|
|
174
181
|
if (payload.type !== "deployment-request") return null;
|
|
175
|
-
const
|
|
176
|
-
if (Math.abs(
|
|
182
|
+
const now3 = Math.floor(Date.now() / 1e3);
|
|
183
|
+
if (Math.abs(now3 - payload.timestamp) > TIMESTAMP_TOLERANCE_SECONDS) return null;
|
|
177
184
|
return payload;
|
|
178
185
|
}
|
|
179
186
|
|
|
@@ -1475,6 +1482,224 @@ function createMessages() {
|
|
|
1475
1482
|
};
|
|
1476
1483
|
}
|
|
1477
1484
|
|
|
1485
|
+
// src/simulator/edge.ts
|
|
1486
|
+
function edgeFailure(error, status = 400, code) {
|
|
1487
|
+
return json({ success: false, error, ...code ? { code } : {} }, status);
|
|
1488
|
+
}
|
|
1489
|
+
function bindingNotFound(alias) {
|
|
1490
|
+
return edgeFailure(`No peripheral is paired to alias "${alias}"`, 404, "BINDING_NOT_FOUND");
|
|
1491
|
+
}
|
|
1492
|
+
function now2() {
|
|
1493
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1494
|
+
}
|
|
1495
|
+
function nextJobId() {
|
|
1496
|
+
state.edgePrintCounter += 1;
|
|
1497
|
+
return `job_${state.edgePrintCounter}`;
|
|
1498
|
+
}
|
|
1499
|
+
function nextConfigVersion() {
|
|
1500
|
+
return state.edgeDisplays.length + 1;
|
|
1501
|
+
}
|
|
1502
|
+
function findPeripheral(id) {
|
|
1503
|
+
return state.edgePeripherals.find((p) => p.id === id);
|
|
1504
|
+
}
|
|
1505
|
+
function buildBinding(alias, peripheralId) {
|
|
1506
|
+
const peripheral = findPeripheral(peripheralId);
|
|
1507
|
+
return {
|
|
1508
|
+
alias,
|
|
1509
|
+
state: peripheral ? "ok" : "peripheral_missing",
|
|
1510
|
+
peripheral: peripheral ? {
|
|
1511
|
+
id: peripheral.id,
|
|
1512
|
+
displayName: peripheral.displayName,
|
|
1513
|
+
driver: peripheral.driver,
|
|
1514
|
+
connected: peripheral.connected
|
|
1515
|
+
} : null,
|
|
1516
|
+
device: peripheral ? {
|
|
1517
|
+
id: peripheral.device.id,
|
|
1518
|
+
displayName: peripheral.device.displayName,
|
|
1519
|
+
status: peripheral.device.status
|
|
1520
|
+
} : null,
|
|
1521
|
+
updatedAt: now2()
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
function handleListDevices() {
|
|
1525
|
+
return success({ devices: [...state.edgeDevices] });
|
|
1526
|
+
}
|
|
1527
|
+
function handleListPeripherals(request) {
|
|
1528
|
+
const deviceId = new URL(request.url).searchParams.get("deviceId");
|
|
1529
|
+
const peripherals = deviceId ? state.edgePeripherals.filter((p) => p.device.id === deviceId) : [...state.edgePeripherals];
|
|
1530
|
+
return success({ peripherals });
|
|
1531
|
+
}
|
|
1532
|
+
function handleListBindings() {
|
|
1533
|
+
return success({ bindings: [...state.edgeBindings.values()] });
|
|
1534
|
+
}
|
|
1535
|
+
function handleGetBinding(alias) {
|
|
1536
|
+
const binding = state.edgeBindings.get(alias);
|
|
1537
|
+
if (!binding) return bindingNotFound(alias);
|
|
1538
|
+
return success({ binding });
|
|
1539
|
+
}
|
|
1540
|
+
async function handlePair(request) {
|
|
1541
|
+
const body = await readJsonBody(request);
|
|
1542
|
+
const alias = String(body.alias ?? "");
|
|
1543
|
+
const peripheralId = String(body.peripheralId ?? "");
|
|
1544
|
+
if (!alias) return edgeFailure("alias is required");
|
|
1545
|
+
if (!peripheralId) return edgeFailure("peripheralId is required");
|
|
1546
|
+
if (!findPeripheral(peripheralId)) {
|
|
1547
|
+
return edgeFailure("Peripheral not found or its device is not granted to this project", 404);
|
|
1548
|
+
}
|
|
1549
|
+
const binding = buildBinding(alias, peripheralId);
|
|
1550
|
+
state.edgeBindings.set(alias, binding);
|
|
1551
|
+
return success({ binding });
|
|
1552
|
+
}
|
|
1553
|
+
function handleUnpair(alias) {
|
|
1554
|
+
const existed = state.edgeBindings.delete(alias);
|
|
1555
|
+
if (!existed) return bindingNotFound(alias);
|
|
1556
|
+
return success({ deleted: true });
|
|
1557
|
+
}
|
|
1558
|
+
async function handlePrint(request) {
|
|
1559
|
+
const body = await readJsonBody(request);
|
|
1560
|
+
const jobId = nextJobId();
|
|
1561
|
+
const captured = {
|
|
1562
|
+
jobId,
|
|
1563
|
+
deploymentId: String(body.deploymentId ?? ""),
|
|
1564
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1565
|
+
deviceId: body.deviceId ? String(body.deviceId) : void 0,
|
|
1566
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0,
|
|
1567
|
+
receipt: body.receipt ?? {},
|
|
1568
|
+
openDrawer: body.openDrawer === true,
|
|
1569
|
+
logo: body.logo === true ? true : body.logo === false ? false : void 0,
|
|
1570
|
+
copies: typeof body.copies === "number" ? body.copies : 1
|
|
1571
|
+
};
|
|
1572
|
+
state.edgePrints.push(captured);
|
|
1573
|
+
return success({
|
|
1574
|
+
jobId,
|
|
1575
|
+
status: "completed"
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
async function handleShowDisplay(request) {
|
|
1579
|
+
const body = await readJsonBody(request);
|
|
1580
|
+
const captured = {
|
|
1581
|
+
action: "show",
|
|
1582
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1583
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0,
|
|
1584
|
+
url: body.url ? String(body.url) : void 0
|
|
1585
|
+
};
|
|
1586
|
+
state.edgeDisplays.push(captured);
|
|
1587
|
+
return success({
|
|
1588
|
+
configVersion: nextConfigVersion(),
|
|
1589
|
+
pushed: true,
|
|
1590
|
+
state: "showing"
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
function handleClearDisplay(request) {
|
|
1594
|
+
const url = new URL(request.url);
|
|
1595
|
+
const alias = url.searchParams.get("alias");
|
|
1596
|
+
const peripheralId = url.searchParams.get("peripheralId");
|
|
1597
|
+
const captured = {
|
|
1598
|
+
action: "clear",
|
|
1599
|
+
alias: alias ?? void 0,
|
|
1600
|
+
peripheralId: peripheralId ?? void 0
|
|
1601
|
+
};
|
|
1602
|
+
state.edgeDisplays.push(captured);
|
|
1603
|
+
return success({
|
|
1604
|
+
configVersion: nextConfigVersion(),
|
|
1605
|
+
pushed: true,
|
|
1606
|
+
state: "cleared"
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
async function handleTestPrint(request) {
|
|
1610
|
+
const body = await readJsonBody(request);
|
|
1611
|
+
const captured = {
|
|
1612
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1613
|
+
deviceId: body.deviceId ? String(body.deviceId) : void 0,
|
|
1614
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0
|
|
1615
|
+
};
|
|
1616
|
+
state.edgeTestPrints.push(captured);
|
|
1617
|
+
return success({
|
|
1618
|
+
status: "ok",
|
|
1619
|
+
peripheralId: captured.peripheralId ?? captured.alias ?? "default"
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
async function handleEdgeRequest(request, subPath) {
|
|
1623
|
+
const method = request.method;
|
|
1624
|
+
if (subPath === "/print" && method === "POST") {
|
|
1625
|
+
return handlePrint(request);
|
|
1626
|
+
}
|
|
1627
|
+
if (subPath === "/devices" && method === "GET") {
|
|
1628
|
+
return handleListDevices();
|
|
1629
|
+
}
|
|
1630
|
+
if (subPath === "/peripherals" && method === "GET") {
|
|
1631
|
+
return handleListPeripherals(request);
|
|
1632
|
+
}
|
|
1633
|
+
if (subPath === "/bindings" && method === "GET") {
|
|
1634
|
+
return handleListBindings();
|
|
1635
|
+
}
|
|
1636
|
+
if (subPath === "/bindings" && method === "PUT") {
|
|
1637
|
+
return handlePair(request);
|
|
1638
|
+
}
|
|
1639
|
+
const bindingMatch = subPath.match(/^\/bindings\/([^/]+)$/);
|
|
1640
|
+
if (bindingMatch) {
|
|
1641
|
+
const alias = decodeURIComponent(bindingMatch[1]);
|
|
1642
|
+
if (method === "GET") return handleGetBinding(alias);
|
|
1643
|
+
if (method === "DELETE") return handleUnpair(alias);
|
|
1644
|
+
}
|
|
1645
|
+
if (subPath === "/display" && method === "POST") {
|
|
1646
|
+
return handleShowDisplay(request);
|
|
1647
|
+
}
|
|
1648
|
+
if (subPath === "/display" && method === "DELETE") {
|
|
1649
|
+
return handleClearDisplay(request);
|
|
1650
|
+
}
|
|
1651
|
+
if (subPath === "/test-print" && method === "POST") {
|
|
1652
|
+
return handleTestPrint(request);
|
|
1653
|
+
}
|
|
1654
|
+
return edgeFailure(`No edge simulator for ${method} .../edge${subPath}`, 404);
|
|
1655
|
+
}
|
|
1656
|
+
function createEdge() {
|
|
1657
|
+
return {
|
|
1658
|
+
get prints() {
|
|
1659
|
+
return [...state.edgePrints];
|
|
1660
|
+
},
|
|
1661
|
+
get displays() {
|
|
1662
|
+
return [...state.edgeDisplays];
|
|
1663
|
+
},
|
|
1664
|
+
get testPrints() {
|
|
1665
|
+
return [...state.edgeTestPrints];
|
|
1666
|
+
},
|
|
1667
|
+
latestPrint() {
|
|
1668
|
+
return state.edgePrints[state.edgePrints.length - 1];
|
|
1669
|
+
},
|
|
1670
|
+
latestDisplay() {
|
|
1671
|
+
return state.edgeDisplays[state.edgeDisplays.length - 1];
|
|
1672
|
+
},
|
|
1673
|
+
get bindings() {
|
|
1674
|
+
return [...state.edgeBindings.values()];
|
|
1675
|
+
},
|
|
1676
|
+
seedDevices(devices) {
|
|
1677
|
+
state.edgeDevices = [...devices];
|
|
1678
|
+
},
|
|
1679
|
+
seedPeripherals(peripherals) {
|
|
1680
|
+
state.edgePeripherals = [...peripherals];
|
|
1681
|
+
},
|
|
1682
|
+
seedBindings(bindings) {
|
|
1683
|
+
state.edgeBindings.clear();
|
|
1684
|
+
for (const binding of bindings) {
|
|
1685
|
+
state.edgeBindings.set(binding.alias, binding);
|
|
1686
|
+
}
|
|
1687
|
+
},
|
|
1688
|
+
clear() {
|
|
1689
|
+
state.edgePrints = [];
|
|
1690
|
+
state.edgeDisplays = [];
|
|
1691
|
+
state.edgeTestPrints = [];
|
|
1692
|
+
state.edgePrintCounter = 0;
|
|
1693
|
+
state.edgeDevices = [];
|
|
1694
|
+
state.edgePeripherals = [];
|
|
1695
|
+
state.edgeBindings.clear();
|
|
1696
|
+
},
|
|
1697
|
+
get count() {
|
|
1698
|
+
return state.edgePrints.length + state.edgeDisplays.length + state.edgeTestPrints.length;
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1478
1703
|
// src/simulator/router.ts
|
|
1479
1704
|
var fetchHolder = globalSingleton("fetch-holder", () => ({
|
|
1480
1705
|
originalFetch: null
|
|
@@ -1495,7 +1720,8 @@ function requiresDeploymentHmac(request, url) {
|
|
|
1495
1720
|
const messagingMatch = url.pathname.match(
|
|
1496
1721
|
/^\/api\/deployments\/[^/]+\/integrations\/(slack|line|facebook)(\/.*)?$/
|
|
1497
1722
|
);
|
|
1498
|
-
|
|
1723
|
+
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
1724
|
+
return !!(dataStoreMatch || isEmail || identitiesMatch || storeMatch || messagingMatch || edgeMatch);
|
|
1499
1725
|
}
|
|
1500
1726
|
async function handleSimulatedRequest(request, url) {
|
|
1501
1727
|
if (url.pathname === "/sql") {
|
|
@@ -1512,6 +1738,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1512
1738
|
const messagingMatch = url.pathname.match(
|
|
1513
1739
|
/^\/api\/deployments\/[^/]+\/integrations\/(slack|line|facebook)(\/.*)?$/
|
|
1514
1740
|
);
|
|
1741
|
+
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
1515
1742
|
const isStorageHost = url.hostname === STORAGE_TEST_HOST;
|
|
1516
1743
|
if (requiresDeploymentHmac(request, url)) {
|
|
1517
1744
|
const authHeader = request.headers.get("X-Stardeck-Auth");
|
|
@@ -1539,6 +1766,9 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1539
1766
|
const channel = messagingMatch[1];
|
|
1540
1767
|
return handleMessagingRequest(request, channel, messagingMatch[2] ?? "");
|
|
1541
1768
|
}
|
|
1769
|
+
if (edgeMatch) {
|
|
1770
|
+
return handleEdgeRequest(request, edgeMatch[1] ?? "");
|
|
1771
|
+
}
|
|
1542
1772
|
if (dataStoreMatch) {
|
|
1543
1773
|
const subPath = dataStoreMatch[1] ?? "";
|
|
1544
1774
|
const db = requireDb();
|
|
@@ -1560,7 +1790,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1560
1790
|
}
|
|
1561
1791
|
}
|
|
1562
1792
|
return failure(
|
|
1563
|
-
`[stardeck-testing] No simulator for ${request.method} ${url.pathname}. Supported: data-store query/mutate/schema, email send, identities CRUD, payments store/checkout, storage upload/files, messaging send, auth verify/refresh, Neon /sql.`,
|
|
1793
|
+
`[stardeck-testing] No simulator for ${request.method} ${url.pathname}. Supported: data-store query/mutate/schema, email send, identities CRUD, payments store/checkout, storage upload/files, messaging send, edge print/display/bindings, auth verify/refresh, Neon /sql.`,
|
|
1564
1794
|
404
|
|
1565
1795
|
);
|
|
1566
1796
|
}
|
|
@@ -1649,6 +1879,7 @@ async function createTestApp(options = {}) {
|
|
|
1649
1879
|
const payments = createPayments();
|
|
1650
1880
|
const storage = createStorage();
|
|
1651
1881
|
const messages = createMessages();
|
|
1882
|
+
const edge = createEdge();
|
|
1652
1883
|
const app = {
|
|
1653
1884
|
db,
|
|
1654
1885
|
inbox,
|
|
@@ -1656,6 +1887,7 @@ async function createTestApp(options = {}) {
|
|
|
1656
1887
|
payments,
|
|
1657
1888
|
storage,
|
|
1658
1889
|
messages,
|
|
1890
|
+
edge,
|
|
1659
1891
|
async query(sql, params = []) {
|
|
1660
1892
|
const result = await db.query(sql, params);
|
|
1661
1893
|
return result.rows;
|
|
@@ -1691,6 +1923,7 @@ async function createTestApp(options = {}) {
|
|
|
1691
1923
|
payments.clear();
|
|
1692
1924
|
storage.clear();
|
|
1693
1925
|
messages.clear();
|
|
1926
|
+
edge.clear();
|
|
1694
1927
|
},
|
|
1695
1928
|
async close() {
|
|
1696
1929
|
state.db = null;
|
|
@@ -1704,6 +1937,7 @@ async function createTestApp(options = {}) {
|
|
|
1704
1937
|
payments.clear();
|
|
1705
1938
|
storage.clear();
|
|
1706
1939
|
messages.clear();
|
|
1940
|
+
edge.clear();
|
|
1707
1941
|
uninstallFetchRouter();
|
|
1708
1942
|
await db.close();
|
|
1709
1943
|
}
|