@stardeck-customer-apps/testing 0.4.0 → 0.5.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/SKILL.md +33 -1
- package/dist/index.d.mts +107 -1
- package/dist/index.d.ts +107 -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/dist/index.mjs
CHANGED
|
@@ -64,6 +64,13 @@ var state = globalSingleton("state", () => ({
|
|
|
64
64
|
storageFiles: /* @__PURE__ */ new Map(),
|
|
65
65
|
presignPending: /* @__PURE__ */ new Map(),
|
|
66
66
|
messages: [],
|
|
67
|
+
edgePrints: [],
|
|
68
|
+
edgeDisplays: [],
|
|
69
|
+
edgeTestPrints: [],
|
|
70
|
+
edgePrintCounter: 0,
|
|
71
|
+
edgeDevices: [],
|
|
72
|
+
edgePeripherals: [],
|
|
73
|
+
edgeBindings: /* @__PURE__ */ new Map(),
|
|
67
74
|
allowNetwork: false
|
|
68
75
|
}));
|
|
69
76
|
function requireDb() {
|
|
@@ -126,8 +133,8 @@ function verifyDeploymentAuthHeader(secret, header) {
|
|
|
126
133
|
return null;
|
|
127
134
|
}
|
|
128
135
|
if (payload.type !== "deployment-request") return null;
|
|
129
|
-
const
|
|
130
|
-
if (Math.abs(
|
|
136
|
+
const now3 = Math.floor(Date.now() / 1e3);
|
|
137
|
+
if (Math.abs(now3 - payload.timestamp) > TIMESTAMP_TOLERANCE_SECONDS) return null;
|
|
131
138
|
return payload;
|
|
132
139
|
}
|
|
133
140
|
|
|
@@ -1429,6 +1436,224 @@ function createMessages() {
|
|
|
1429
1436
|
};
|
|
1430
1437
|
}
|
|
1431
1438
|
|
|
1439
|
+
// src/simulator/edge.ts
|
|
1440
|
+
function edgeFailure(error, status = 400, code) {
|
|
1441
|
+
return json({ success: false, error, ...code ? { code } : {} }, status);
|
|
1442
|
+
}
|
|
1443
|
+
function bindingNotFound(alias) {
|
|
1444
|
+
return edgeFailure(`No peripheral is paired to alias "${alias}"`, 404, "BINDING_NOT_FOUND");
|
|
1445
|
+
}
|
|
1446
|
+
function now2() {
|
|
1447
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1448
|
+
}
|
|
1449
|
+
function nextJobId() {
|
|
1450
|
+
state.edgePrintCounter += 1;
|
|
1451
|
+
return `job_${state.edgePrintCounter}`;
|
|
1452
|
+
}
|
|
1453
|
+
function nextConfigVersion() {
|
|
1454
|
+
return state.edgeDisplays.length + 1;
|
|
1455
|
+
}
|
|
1456
|
+
function findPeripheral(id) {
|
|
1457
|
+
return state.edgePeripherals.find((p) => p.id === id);
|
|
1458
|
+
}
|
|
1459
|
+
function buildBinding(alias, peripheralId) {
|
|
1460
|
+
const peripheral = findPeripheral(peripheralId);
|
|
1461
|
+
return {
|
|
1462
|
+
alias,
|
|
1463
|
+
state: peripheral ? "ok" : "peripheral_missing",
|
|
1464
|
+
peripheral: peripheral ? {
|
|
1465
|
+
id: peripheral.id,
|
|
1466
|
+
displayName: peripheral.displayName,
|
|
1467
|
+
driver: peripheral.driver,
|
|
1468
|
+
connected: peripheral.connected
|
|
1469
|
+
} : null,
|
|
1470
|
+
device: peripheral ? {
|
|
1471
|
+
id: peripheral.device.id,
|
|
1472
|
+
displayName: peripheral.device.displayName,
|
|
1473
|
+
status: peripheral.device.status
|
|
1474
|
+
} : null,
|
|
1475
|
+
updatedAt: now2()
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
function handleListDevices() {
|
|
1479
|
+
return success({ devices: [...state.edgeDevices] });
|
|
1480
|
+
}
|
|
1481
|
+
function handleListPeripherals(request) {
|
|
1482
|
+
const deviceId = new URL(request.url).searchParams.get("deviceId");
|
|
1483
|
+
const peripherals = deviceId ? state.edgePeripherals.filter((p) => p.device.id === deviceId) : [...state.edgePeripherals];
|
|
1484
|
+
return success({ peripherals });
|
|
1485
|
+
}
|
|
1486
|
+
function handleListBindings() {
|
|
1487
|
+
return success({ bindings: [...state.edgeBindings.values()] });
|
|
1488
|
+
}
|
|
1489
|
+
function handleGetBinding(alias) {
|
|
1490
|
+
const binding = state.edgeBindings.get(alias);
|
|
1491
|
+
if (!binding) return bindingNotFound(alias);
|
|
1492
|
+
return success({ binding });
|
|
1493
|
+
}
|
|
1494
|
+
async function handlePair(request) {
|
|
1495
|
+
const body = await readJsonBody(request);
|
|
1496
|
+
const alias = String(body.alias ?? "");
|
|
1497
|
+
const peripheralId = String(body.peripheralId ?? "");
|
|
1498
|
+
if (!alias) return edgeFailure("alias is required");
|
|
1499
|
+
if (!peripheralId) return edgeFailure("peripheralId is required");
|
|
1500
|
+
if (!findPeripheral(peripheralId)) {
|
|
1501
|
+
return edgeFailure("Peripheral not found or its device is not granted to this project", 404);
|
|
1502
|
+
}
|
|
1503
|
+
const binding = buildBinding(alias, peripheralId);
|
|
1504
|
+
state.edgeBindings.set(alias, binding);
|
|
1505
|
+
return success({ binding });
|
|
1506
|
+
}
|
|
1507
|
+
function handleUnpair(alias) {
|
|
1508
|
+
const existed = state.edgeBindings.delete(alias);
|
|
1509
|
+
if (!existed) return bindingNotFound(alias);
|
|
1510
|
+
return success({ deleted: true });
|
|
1511
|
+
}
|
|
1512
|
+
async function handlePrint(request) {
|
|
1513
|
+
const body = await readJsonBody(request);
|
|
1514
|
+
const jobId = nextJobId();
|
|
1515
|
+
const captured = {
|
|
1516
|
+
jobId,
|
|
1517
|
+
deploymentId: String(body.deploymentId ?? ""),
|
|
1518
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1519
|
+
deviceId: body.deviceId ? String(body.deviceId) : void 0,
|
|
1520
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0,
|
|
1521
|
+
receipt: body.receipt ?? {},
|
|
1522
|
+
openDrawer: body.openDrawer === true,
|
|
1523
|
+
logo: body.logo === true ? true : body.logo === false ? false : void 0,
|
|
1524
|
+
copies: typeof body.copies === "number" ? body.copies : 1
|
|
1525
|
+
};
|
|
1526
|
+
state.edgePrints.push(captured);
|
|
1527
|
+
return success({
|
|
1528
|
+
jobId,
|
|
1529
|
+
status: "completed"
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
async function handleShowDisplay(request) {
|
|
1533
|
+
const body = await readJsonBody(request);
|
|
1534
|
+
const captured = {
|
|
1535
|
+
action: "show",
|
|
1536
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1537
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0,
|
|
1538
|
+
url: body.url ? String(body.url) : void 0
|
|
1539
|
+
};
|
|
1540
|
+
state.edgeDisplays.push(captured);
|
|
1541
|
+
return success({
|
|
1542
|
+
configVersion: nextConfigVersion(),
|
|
1543
|
+
pushed: true,
|
|
1544
|
+
state: "showing"
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
function handleClearDisplay(request) {
|
|
1548
|
+
const url = new URL(request.url);
|
|
1549
|
+
const alias = url.searchParams.get("alias");
|
|
1550
|
+
const peripheralId = url.searchParams.get("peripheralId");
|
|
1551
|
+
const captured = {
|
|
1552
|
+
action: "clear",
|
|
1553
|
+
alias: alias ?? void 0,
|
|
1554
|
+
peripheralId: peripheralId ?? void 0
|
|
1555
|
+
};
|
|
1556
|
+
state.edgeDisplays.push(captured);
|
|
1557
|
+
return success({
|
|
1558
|
+
configVersion: nextConfigVersion(),
|
|
1559
|
+
pushed: true,
|
|
1560
|
+
state: "cleared"
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
async function handleTestPrint(request) {
|
|
1564
|
+
const body = await readJsonBody(request);
|
|
1565
|
+
const captured = {
|
|
1566
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1567
|
+
deviceId: body.deviceId ? String(body.deviceId) : void 0,
|
|
1568
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0
|
|
1569
|
+
};
|
|
1570
|
+
state.edgeTestPrints.push(captured);
|
|
1571
|
+
return success({
|
|
1572
|
+
status: "ok",
|
|
1573
|
+
peripheralId: captured.peripheralId ?? captured.alias ?? "default"
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
async function handleEdgeRequest(request, subPath) {
|
|
1577
|
+
const method = request.method;
|
|
1578
|
+
if (subPath === "/print" && method === "POST") {
|
|
1579
|
+
return handlePrint(request);
|
|
1580
|
+
}
|
|
1581
|
+
if (subPath === "/devices" && method === "GET") {
|
|
1582
|
+
return handleListDevices();
|
|
1583
|
+
}
|
|
1584
|
+
if (subPath === "/peripherals" && method === "GET") {
|
|
1585
|
+
return handleListPeripherals(request);
|
|
1586
|
+
}
|
|
1587
|
+
if (subPath === "/bindings" && method === "GET") {
|
|
1588
|
+
return handleListBindings();
|
|
1589
|
+
}
|
|
1590
|
+
if (subPath === "/bindings" && method === "PUT") {
|
|
1591
|
+
return handlePair(request);
|
|
1592
|
+
}
|
|
1593
|
+
const bindingMatch = subPath.match(/^\/bindings\/([^/]+)$/);
|
|
1594
|
+
if (bindingMatch) {
|
|
1595
|
+
const alias = decodeURIComponent(bindingMatch[1]);
|
|
1596
|
+
if (method === "GET") return handleGetBinding(alias);
|
|
1597
|
+
if (method === "DELETE") return handleUnpair(alias);
|
|
1598
|
+
}
|
|
1599
|
+
if (subPath === "/display" && method === "POST") {
|
|
1600
|
+
return handleShowDisplay(request);
|
|
1601
|
+
}
|
|
1602
|
+
if (subPath === "/display" && method === "DELETE") {
|
|
1603
|
+
return handleClearDisplay(request);
|
|
1604
|
+
}
|
|
1605
|
+
if (subPath === "/test-print" && method === "POST") {
|
|
1606
|
+
return handleTestPrint(request);
|
|
1607
|
+
}
|
|
1608
|
+
return edgeFailure(`No edge simulator for ${method} .../edge${subPath}`, 404);
|
|
1609
|
+
}
|
|
1610
|
+
function createEdge() {
|
|
1611
|
+
return {
|
|
1612
|
+
get prints() {
|
|
1613
|
+
return [...state.edgePrints];
|
|
1614
|
+
},
|
|
1615
|
+
get displays() {
|
|
1616
|
+
return [...state.edgeDisplays];
|
|
1617
|
+
},
|
|
1618
|
+
get testPrints() {
|
|
1619
|
+
return [...state.edgeTestPrints];
|
|
1620
|
+
},
|
|
1621
|
+
latestPrint() {
|
|
1622
|
+
return state.edgePrints[state.edgePrints.length - 1];
|
|
1623
|
+
},
|
|
1624
|
+
latestDisplay() {
|
|
1625
|
+
return state.edgeDisplays[state.edgeDisplays.length - 1];
|
|
1626
|
+
},
|
|
1627
|
+
get bindings() {
|
|
1628
|
+
return [...state.edgeBindings.values()];
|
|
1629
|
+
},
|
|
1630
|
+
seedDevices(devices) {
|
|
1631
|
+
state.edgeDevices = [...devices];
|
|
1632
|
+
},
|
|
1633
|
+
seedPeripherals(peripherals) {
|
|
1634
|
+
state.edgePeripherals = [...peripherals];
|
|
1635
|
+
},
|
|
1636
|
+
seedBindings(bindings) {
|
|
1637
|
+
state.edgeBindings.clear();
|
|
1638
|
+
for (const binding of bindings) {
|
|
1639
|
+
state.edgeBindings.set(binding.alias, binding);
|
|
1640
|
+
}
|
|
1641
|
+
},
|
|
1642
|
+
clear() {
|
|
1643
|
+
state.edgePrints = [];
|
|
1644
|
+
state.edgeDisplays = [];
|
|
1645
|
+
state.edgeTestPrints = [];
|
|
1646
|
+
state.edgePrintCounter = 0;
|
|
1647
|
+
state.edgeDevices = [];
|
|
1648
|
+
state.edgePeripherals = [];
|
|
1649
|
+
state.edgeBindings.clear();
|
|
1650
|
+
},
|
|
1651
|
+
get count() {
|
|
1652
|
+
return state.edgePrints.length + state.edgeDisplays.length + state.edgeTestPrints.length;
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1432
1657
|
// src/simulator/router.ts
|
|
1433
1658
|
var fetchHolder = globalSingleton("fetch-holder", () => ({
|
|
1434
1659
|
originalFetch: null
|
|
@@ -1449,7 +1674,8 @@ function requiresDeploymentHmac(request, url) {
|
|
|
1449
1674
|
const messagingMatch = url.pathname.match(
|
|
1450
1675
|
/^\/api\/deployments\/[^/]+\/integrations\/(slack|line|facebook)(\/.*)?$/
|
|
1451
1676
|
);
|
|
1452
|
-
|
|
1677
|
+
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
1678
|
+
return !!(dataStoreMatch || isEmail || identitiesMatch || storeMatch || messagingMatch || edgeMatch);
|
|
1453
1679
|
}
|
|
1454
1680
|
async function handleSimulatedRequest(request, url) {
|
|
1455
1681
|
if (url.pathname === "/sql") {
|
|
@@ -1466,6 +1692,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1466
1692
|
const messagingMatch = url.pathname.match(
|
|
1467
1693
|
/^\/api\/deployments\/[^/]+\/integrations\/(slack|line|facebook)(\/.*)?$/
|
|
1468
1694
|
);
|
|
1695
|
+
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
1469
1696
|
const isStorageHost = url.hostname === STORAGE_TEST_HOST;
|
|
1470
1697
|
if (requiresDeploymentHmac(request, url)) {
|
|
1471
1698
|
const authHeader = request.headers.get("X-Stardeck-Auth");
|
|
@@ -1493,6 +1720,9 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1493
1720
|
const channel = messagingMatch[1];
|
|
1494
1721
|
return handleMessagingRequest(request, channel, messagingMatch[2] ?? "");
|
|
1495
1722
|
}
|
|
1723
|
+
if (edgeMatch) {
|
|
1724
|
+
return handleEdgeRequest(request, edgeMatch[1] ?? "");
|
|
1725
|
+
}
|
|
1496
1726
|
if (dataStoreMatch) {
|
|
1497
1727
|
const subPath = dataStoreMatch[1] ?? "";
|
|
1498
1728
|
const db = requireDb();
|
|
@@ -1514,7 +1744,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1514
1744
|
}
|
|
1515
1745
|
}
|
|
1516
1746
|
return failure(
|
|
1517
|
-
`[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.`,
|
|
1747
|
+
`[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.`,
|
|
1518
1748
|
404
|
|
1519
1749
|
);
|
|
1520
1750
|
}
|
|
@@ -1603,6 +1833,7 @@ async function createTestApp(options = {}) {
|
|
|
1603
1833
|
const payments = createPayments();
|
|
1604
1834
|
const storage = createStorage();
|
|
1605
1835
|
const messages = createMessages();
|
|
1836
|
+
const edge = createEdge();
|
|
1606
1837
|
const app = {
|
|
1607
1838
|
db,
|
|
1608
1839
|
inbox,
|
|
@@ -1610,6 +1841,7 @@ async function createTestApp(options = {}) {
|
|
|
1610
1841
|
payments,
|
|
1611
1842
|
storage,
|
|
1612
1843
|
messages,
|
|
1844
|
+
edge,
|
|
1613
1845
|
async query(sql, params = []) {
|
|
1614
1846
|
const result = await db.query(sql, params);
|
|
1615
1847
|
return result.rows;
|
|
@@ -1645,6 +1877,7 @@ async function createTestApp(options = {}) {
|
|
|
1645
1877
|
payments.clear();
|
|
1646
1878
|
storage.clear();
|
|
1647
1879
|
messages.clear();
|
|
1880
|
+
edge.clear();
|
|
1648
1881
|
},
|
|
1649
1882
|
async close() {
|
|
1650
1883
|
state.db = null;
|
|
@@ -1658,6 +1891,7 @@ async function createTestApp(options = {}) {
|
|
|
1658
1891
|
payments.clear();
|
|
1659
1892
|
storage.clear();
|
|
1660
1893
|
messages.clear();
|
|
1894
|
+
edge.clear();
|
|
1661
1895
|
uninstallFetchRouter();
|
|
1662
1896
|
await db.close();
|
|
1663
1897
|
}
|
|
@@ -56,6 +56,13 @@ var state = globalSingleton("state", () => ({
|
|
|
56
56
|
storageFiles: /* @__PURE__ */ new Map(),
|
|
57
57
|
presignPending: /* @__PURE__ */ new Map(),
|
|
58
58
|
messages: [],
|
|
59
|
+
edgePrints: [],
|
|
60
|
+
edgeDisplays: [],
|
|
61
|
+
edgeTestPrints: [],
|
|
62
|
+
edgePrintCounter: 0,
|
|
63
|
+
edgeDevices: [],
|
|
64
|
+
edgePeripherals: [],
|
|
65
|
+
edgeBindings: /* @__PURE__ */ new Map(),
|
|
59
66
|
allowNetwork: false
|
|
60
67
|
}));
|
|
61
68
|
|
|
@@ -29,6 +29,13 @@ var state = globalSingleton("state", () => ({
|
|
|
29
29
|
storageFiles: /* @__PURE__ */ new Map(),
|
|
30
30
|
presignPending: /* @__PURE__ */ new Map(),
|
|
31
31
|
messages: [],
|
|
32
|
+
edgePrints: [],
|
|
33
|
+
edgeDisplays: [],
|
|
34
|
+
edgeTestPrints: [],
|
|
35
|
+
edgePrintCounter: 0,
|
|
36
|
+
edgeDevices: [],
|
|
37
|
+
edgePeripherals: [],
|
|
38
|
+
edgeBindings: /* @__PURE__ */ new Map(),
|
|
32
39
|
allowNetwork: false
|
|
33
40
|
}));
|
|
34
41
|
|
package/dist/setup.js
CHANGED
|
@@ -50,6 +50,13 @@ var state = globalSingleton("state", () => ({
|
|
|
50
50
|
storageFiles: /* @__PURE__ */ new Map(),
|
|
51
51
|
presignPending: /* @__PURE__ */ new Map(),
|
|
52
52
|
messages: [],
|
|
53
|
+
edgePrints: [],
|
|
54
|
+
edgeDisplays: [],
|
|
55
|
+
edgeTestPrints: [],
|
|
56
|
+
edgePrintCounter: 0,
|
|
57
|
+
edgeDevices: [],
|
|
58
|
+
edgePeripherals: [],
|
|
59
|
+
edgeBindings: /* @__PURE__ */ new Map(),
|
|
53
60
|
allowNetwork: false
|
|
54
61
|
}));
|
|
55
62
|
function requireDb() {
|
|
@@ -104,8 +111,8 @@ function verifyDeploymentAuthHeader(secret, header) {
|
|
|
104
111
|
return null;
|
|
105
112
|
}
|
|
106
113
|
if (payload.type !== "deployment-request") return null;
|
|
107
|
-
const
|
|
108
|
-
if (Math.abs(
|
|
114
|
+
const now3 = Math.floor(Date.now() / 1e3);
|
|
115
|
+
if (Math.abs(now3 - payload.timestamp) > TIMESTAMP_TOLERANCE_SECONDS) return null;
|
|
109
116
|
return payload;
|
|
110
117
|
}
|
|
111
118
|
|
|
@@ -1159,6 +1166,178 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1159
1166
|
return failure(`No messaging simulator for ${method} .../integrations/${channel}${subPath}`, 404);
|
|
1160
1167
|
}
|
|
1161
1168
|
|
|
1169
|
+
// src/simulator/edge.ts
|
|
1170
|
+
function edgeFailure(error, status = 400, code) {
|
|
1171
|
+
return json({ success: false, error, ...code ? { code } : {} }, status);
|
|
1172
|
+
}
|
|
1173
|
+
function bindingNotFound(alias) {
|
|
1174
|
+
return edgeFailure(`No peripheral is paired to alias "${alias}"`, 404, "BINDING_NOT_FOUND");
|
|
1175
|
+
}
|
|
1176
|
+
function now2() {
|
|
1177
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1178
|
+
}
|
|
1179
|
+
function nextJobId() {
|
|
1180
|
+
state.edgePrintCounter += 1;
|
|
1181
|
+
return `job_${state.edgePrintCounter}`;
|
|
1182
|
+
}
|
|
1183
|
+
function nextConfigVersion() {
|
|
1184
|
+
return state.edgeDisplays.length + 1;
|
|
1185
|
+
}
|
|
1186
|
+
function findPeripheral(id) {
|
|
1187
|
+
return state.edgePeripherals.find((p) => p.id === id);
|
|
1188
|
+
}
|
|
1189
|
+
function buildBinding(alias, peripheralId) {
|
|
1190
|
+
const peripheral = findPeripheral(peripheralId);
|
|
1191
|
+
return {
|
|
1192
|
+
alias,
|
|
1193
|
+
state: peripheral ? "ok" : "peripheral_missing",
|
|
1194
|
+
peripheral: peripheral ? {
|
|
1195
|
+
id: peripheral.id,
|
|
1196
|
+
displayName: peripheral.displayName,
|
|
1197
|
+
driver: peripheral.driver,
|
|
1198
|
+
connected: peripheral.connected
|
|
1199
|
+
} : null,
|
|
1200
|
+
device: peripheral ? {
|
|
1201
|
+
id: peripheral.device.id,
|
|
1202
|
+
displayName: peripheral.device.displayName,
|
|
1203
|
+
status: peripheral.device.status
|
|
1204
|
+
} : null,
|
|
1205
|
+
updatedAt: now2()
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
function handleListDevices() {
|
|
1209
|
+
return success({ devices: [...state.edgeDevices] });
|
|
1210
|
+
}
|
|
1211
|
+
function handleListPeripherals(request) {
|
|
1212
|
+
const deviceId = new URL(request.url).searchParams.get("deviceId");
|
|
1213
|
+
const peripherals = deviceId ? state.edgePeripherals.filter((p) => p.device.id === deviceId) : [...state.edgePeripherals];
|
|
1214
|
+
return success({ peripherals });
|
|
1215
|
+
}
|
|
1216
|
+
function handleListBindings() {
|
|
1217
|
+
return success({ bindings: [...state.edgeBindings.values()] });
|
|
1218
|
+
}
|
|
1219
|
+
function handleGetBinding(alias) {
|
|
1220
|
+
const binding = state.edgeBindings.get(alias);
|
|
1221
|
+
if (!binding) return bindingNotFound(alias);
|
|
1222
|
+
return success({ binding });
|
|
1223
|
+
}
|
|
1224
|
+
async function handlePair(request) {
|
|
1225
|
+
const body = await readJsonBody(request);
|
|
1226
|
+
const alias = String(body.alias ?? "");
|
|
1227
|
+
const peripheralId = String(body.peripheralId ?? "");
|
|
1228
|
+
if (!alias) return edgeFailure("alias is required");
|
|
1229
|
+
if (!peripheralId) return edgeFailure("peripheralId is required");
|
|
1230
|
+
if (!findPeripheral(peripheralId)) {
|
|
1231
|
+
return edgeFailure("Peripheral not found or its device is not granted to this project", 404);
|
|
1232
|
+
}
|
|
1233
|
+
const binding = buildBinding(alias, peripheralId);
|
|
1234
|
+
state.edgeBindings.set(alias, binding);
|
|
1235
|
+
return success({ binding });
|
|
1236
|
+
}
|
|
1237
|
+
function handleUnpair(alias) {
|
|
1238
|
+
const existed = state.edgeBindings.delete(alias);
|
|
1239
|
+
if (!existed) return bindingNotFound(alias);
|
|
1240
|
+
return success({ deleted: true });
|
|
1241
|
+
}
|
|
1242
|
+
async function handlePrint(request) {
|
|
1243
|
+
const body = await readJsonBody(request);
|
|
1244
|
+
const jobId = nextJobId();
|
|
1245
|
+
const captured = {
|
|
1246
|
+
jobId,
|
|
1247
|
+
deploymentId: String(body.deploymentId ?? ""),
|
|
1248
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1249
|
+
deviceId: body.deviceId ? String(body.deviceId) : void 0,
|
|
1250
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0,
|
|
1251
|
+
receipt: body.receipt ?? {},
|
|
1252
|
+
openDrawer: body.openDrawer === true,
|
|
1253
|
+
logo: body.logo === true ? true : body.logo === false ? false : void 0,
|
|
1254
|
+
copies: typeof body.copies === "number" ? body.copies : 1
|
|
1255
|
+
};
|
|
1256
|
+
state.edgePrints.push(captured);
|
|
1257
|
+
return success({
|
|
1258
|
+
jobId,
|
|
1259
|
+
status: "completed"
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
async function handleShowDisplay(request) {
|
|
1263
|
+
const body = await readJsonBody(request);
|
|
1264
|
+
const captured = {
|
|
1265
|
+
action: "show",
|
|
1266
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1267
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0,
|
|
1268
|
+
url: body.url ? String(body.url) : void 0
|
|
1269
|
+
};
|
|
1270
|
+
state.edgeDisplays.push(captured);
|
|
1271
|
+
return success({
|
|
1272
|
+
configVersion: nextConfigVersion(),
|
|
1273
|
+
pushed: true,
|
|
1274
|
+
state: "showing"
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
function handleClearDisplay(request) {
|
|
1278
|
+
const url = new URL(request.url);
|
|
1279
|
+
const alias = url.searchParams.get("alias");
|
|
1280
|
+
const peripheralId = url.searchParams.get("peripheralId");
|
|
1281
|
+
const captured = {
|
|
1282
|
+
action: "clear",
|
|
1283
|
+
alias: alias ?? void 0,
|
|
1284
|
+
peripheralId: peripheralId ?? void 0
|
|
1285
|
+
};
|
|
1286
|
+
state.edgeDisplays.push(captured);
|
|
1287
|
+
return success({
|
|
1288
|
+
configVersion: nextConfigVersion(),
|
|
1289
|
+
pushed: true,
|
|
1290
|
+
state: "cleared"
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
async function handleTestPrint(request) {
|
|
1294
|
+
const body = await readJsonBody(request);
|
|
1295
|
+
const captured = {
|
|
1296
|
+
alias: body.alias ? String(body.alias) : void 0,
|
|
1297
|
+
deviceId: body.deviceId ? String(body.deviceId) : void 0,
|
|
1298
|
+
peripheralId: body.peripheralId ? String(body.peripheralId) : void 0
|
|
1299
|
+
};
|
|
1300
|
+
state.edgeTestPrints.push(captured);
|
|
1301
|
+
return success({
|
|
1302
|
+
status: "ok",
|
|
1303
|
+
peripheralId: captured.peripheralId ?? captured.alias ?? "default"
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
async function handleEdgeRequest(request, subPath) {
|
|
1307
|
+
const method = request.method;
|
|
1308
|
+
if (subPath === "/print" && method === "POST") {
|
|
1309
|
+
return handlePrint(request);
|
|
1310
|
+
}
|
|
1311
|
+
if (subPath === "/devices" && method === "GET") {
|
|
1312
|
+
return handleListDevices();
|
|
1313
|
+
}
|
|
1314
|
+
if (subPath === "/peripherals" && method === "GET") {
|
|
1315
|
+
return handleListPeripherals(request);
|
|
1316
|
+
}
|
|
1317
|
+
if (subPath === "/bindings" && method === "GET") {
|
|
1318
|
+
return handleListBindings();
|
|
1319
|
+
}
|
|
1320
|
+
if (subPath === "/bindings" && method === "PUT") {
|
|
1321
|
+
return handlePair(request);
|
|
1322
|
+
}
|
|
1323
|
+
const bindingMatch = subPath.match(/^\/bindings\/([^/]+)$/);
|
|
1324
|
+
if (bindingMatch) {
|
|
1325
|
+
const alias = decodeURIComponent(bindingMatch[1]);
|
|
1326
|
+
if (method === "GET") return handleGetBinding(alias);
|
|
1327
|
+
if (method === "DELETE") return handleUnpair(alias);
|
|
1328
|
+
}
|
|
1329
|
+
if (subPath === "/display" && method === "POST") {
|
|
1330
|
+
return handleShowDisplay(request);
|
|
1331
|
+
}
|
|
1332
|
+
if (subPath === "/display" && method === "DELETE") {
|
|
1333
|
+
return handleClearDisplay(request);
|
|
1334
|
+
}
|
|
1335
|
+
if (subPath === "/test-print" && method === "POST") {
|
|
1336
|
+
return handleTestPrint(request);
|
|
1337
|
+
}
|
|
1338
|
+
return edgeFailure(`No edge simulator for ${method} .../edge${subPath}`, 404);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1162
1341
|
// src/simulator/router.ts
|
|
1163
1342
|
var fetchHolder = globalSingleton("fetch-holder", () => ({
|
|
1164
1343
|
originalFetch: null
|
|
@@ -1179,7 +1358,8 @@ function requiresDeploymentHmac(request, url) {
|
|
|
1179
1358
|
const messagingMatch = url.pathname.match(
|
|
1180
1359
|
/^\/api\/deployments\/[^/]+\/integrations\/(slack|line|facebook)(\/.*)?$/
|
|
1181
1360
|
);
|
|
1182
|
-
|
|
1361
|
+
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
1362
|
+
return !!(dataStoreMatch || isEmail || identitiesMatch || storeMatch || messagingMatch || edgeMatch);
|
|
1183
1363
|
}
|
|
1184
1364
|
async function handleSimulatedRequest(request, url) {
|
|
1185
1365
|
if (url.pathname === "/sql") {
|
|
@@ -1196,6 +1376,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1196
1376
|
const messagingMatch = url.pathname.match(
|
|
1197
1377
|
/^\/api\/deployments\/[^/]+\/integrations\/(slack|line|facebook)(\/.*)?$/
|
|
1198
1378
|
);
|
|
1379
|
+
const edgeMatch = url.pathname.match(/^\/api\/deployments\/[^/]+\/edge(\/.*)?$/);
|
|
1199
1380
|
const isStorageHost = url.hostname === STORAGE_TEST_HOST;
|
|
1200
1381
|
if (requiresDeploymentHmac(request, url)) {
|
|
1201
1382
|
const authHeader = request.headers.get("X-Stardeck-Auth");
|
|
@@ -1223,6 +1404,9 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1223
1404
|
const channel = messagingMatch[1];
|
|
1224
1405
|
return handleMessagingRequest(request, channel, messagingMatch[2] ?? "");
|
|
1225
1406
|
}
|
|
1407
|
+
if (edgeMatch) {
|
|
1408
|
+
return handleEdgeRequest(request, edgeMatch[1] ?? "");
|
|
1409
|
+
}
|
|
1226
1410
|
if (dataStoreMatch) {
|
|
1227
1411
|
const subPath = dataStoreMatch[1] ?? "";
|
|
1228
1412
|
const db = requireDb();
|
|
@@ -1244,7 +1428,7 @@ async function handleSimulatedRequest(request, url) {
|
|
|
1244
1428
|
}
|
|
1245
1429
|
}
|
|
1246
1430
|
return failure(
|
|
1247
|
-
`[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.`,
|
|
1431
|
+
`[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.`,
|
|
1248
1432
|
404
|
|
1249
1433
|
);
|
|
1250
1434
|
}
|