@stardeck-customer-apps/testing 0.7.0 → 0.7.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 CHANGED
@@ -175,7 +175,8 @@ externalId })` helper models a trusted platform/channel link, and
175
175
  `.all()`, `.latest()`, `.to(recipient)`, `.channel("line")`, `.count`, `.clear()`.
176
176
  - `app.edge` — edge print/display/bindings from edge-sdk: `.prints`, `.displays`,
177
177
  `.testPrints`, `.latestPrint()`, `.latestDisplay()`, `.bindings`,
178
- `.seedDevices()`, `.seedPeripherals()`, `.seedBindings()`, `.count`, `.clear()`.
178
+ `.seedDevices()`, `.seedPeripherals()`, `.seedBindings()`,
179
+ `.seedCapabilities(caps, peripheralId?)`, `.count`, `.clear()`.
179
180
  - `app.query(sql, params?)` / `app.db` — direct database access for
180
181
  assertions and seeding.
181
182
  - `callRoute(handler, opts)` — invoke an App Router route handler with a real
@@ -242,8 +243,32 @@ cursor, limit })`; `list()` intentionally keeps its previous array shape.
242
243
  - `client.slack` / `client.line` / `client.facebook` send endpoints — captured
243
244
  in `app.messages`.
244
245
  - `createEdgeClient()` (print, pair/unpair, list devices/peripherals, show/clear
245
- display, test print) — captured in `app.edge`; seed devices/peripherals for
246
- pairing pickers via `app.edge.seedDevices()` / `seedPeripherals()`.
246
+ display, test print, `getCapabilities()`) — captured in `app.edge`; seed
247
+ devices/peripherals for pairing pickers via `app.edge.seedDevices()` /
248
+ `seedPeripherals()`.
249
+
250
+ ### Receipt layout and printer capabilities
251
+
252
+ `edge.getCapabilities(target)` answers with a stock 80mm text-mode printer
253
+ unless the test says otherwise. Seed a different one to exercise layout,
254
+ degradation, and preview code — per peripheral when a test models more than one
255
+ station:
256
+
257
+ ```ts
258
+ app.edge.seedPeripherals([kitchenPrinter, counterPrinter]);
259
+ app.edge.seedCapabilities({ ...caps58mm, renderMode: "raster" }, kitchenPrinter.id);
260
+
261
+ const caps = await edge.getCapabilities({ peripheralId: kitchenPrinter.id });
262
+ expect(caps.columns).toBe(32);
263
+ ```
264
+
265
+ The simulator resolves the target the way production does: an unpaired alias
266
+ throws `BindingError` (`BINDING_NOT_FOUND`) and an unknown peripheral 404s, so
267
+ target-resolution bugs surface in tests rather than in the field.
268
+
269
+ Printed receipts are captured whole, so assert on the ordered blocks an app
270
+ produced — `app.edge.latestPrint()?.receipt.blocks` — not just its totals.
271
+
247
272
  - `next/headers` (`headers()`/`cookies()`) inside handlers under `callRoute`.
248
273
 
249
274
  ## End-to-end (Playwright) suite
package/dist/index.d.mts CHANGED
@@ -204,17 +204,51 @@ interface TestMessages {
204
204
  clear(): void;
205
205
  get count(): number;
206
206
  }
207
+ interface ReceiptItem {
208
+ name: string;
209
+ quantity: number;
210
+ unitPrice: number;
211
+ total: number;
212
+ note?: string;
213
+ }
214
+ type ReceiptAlign = "left" | "center" | "right";
215
+ type ReceiptTextSize = "small" | "normal" | "large";
216
+ /** Mirrors the edge SDK's block model so assertions can inspect the exact
217
+ * ordered content an app printed, not just its totals. */
218
+ type ReceiptBlock = {
219
+ type: "text";
220
+ text: string;
221
+ align?: ReceiptAlign;
222
+ bold?: boolean;
223
+ size?: ReceiptTextSize;
224
+ } | {
225
+ type: "columns";
226
+ left: string;
227
+ right: string;
228
+ bold?: boolean;
229
+ size?: ReceiptTextSize;
230
+ } | {
231
+ type: "separator";
232
+ style?: "dashed" | "solid";
233
+ } | {
234
+ type: "spacer";
235
+ lines?: number;
236
+ } | {
237
+ type: "items";
238
+ rows: ReceiptItem[];
239
+ } | {
240
+ type: "barcode";
241
+ format: "qr" | "code128";
242
+ data: string;
243
+ align?: ReceiptAlign;
244
+ };
207
245
  interface ReceiptPayload {
246
+ paperWidth?: "58mm" | "80mm";
247
+ blocks?: ReceiptBlock[];
208
248
  header?: {
209
249
  lines: string[];
210
250
  };
211
- items?: Array<{
212
- name: string;
213
- quantity: number;
214
- unitPrice: number;
215
- total: number;
216
- note?: string;
217
- }>;
251
+ items?: ReceiptItem[];
218
252
  subtotal?: number;
219
253
  tax?: number;
220
254
  discount?: number;
@@ -232,6 +266,23 @@ interface ReceiptPayload {
232
266
  };
233
267
  rawEscPos?: string;
234
268
  }
269
+ /** Mirrors the edge SDK's PrinterCapabilities so tests can drive
270
+ * `edge.getCapabilities()` without the SDK being a dependency here. */
271
+ interface PrinterCapabilities {
272
+ paperWidth: "58mm" | "80mm";
273
+ columns: number;
274
+ dotsPerLine: number;
275
+ renderMode: "text" | "raster";
276
+ textEncoding: "utf8" | "tis620";
277
+ unicodeText: boolean;
278
+ thaiText: boolean;
279
+ bold: boolean;
280
+ textSizes: ReceiptTextSize[];
281
+ barcodeFormats: Array<"qr" | "code128">;
282
+ rasterImages: boolean;
283
+ cut: boolean;
284
+ cashDrawer: boolean;
285
+ }
235
286
  interface DeviceInfo {
236
287
  id: string;
237
288
  displayName: string;
@@ -301,6 +352,13 @@ interface TestEdge {
301
352
  seedDevices(devices: DeviceInfo[]): void;
302
353
  seedPeripherals(peripherals: PeripheralInfo[]): void;
303
354
  seedBindings(bindings: BindingInfo[]): void;
355
+ /**
356
+ * Answer `edge.getCapabilities()` with a specific printer — e.g. a 58mm or a
357
+ * raster/Thai one — so layout and preview code can be tested against it.
358
+ * Pass a `peripheralId` to model stations with different printers; without
359
+ * one this sets the default every untargeted lookup falls back to.
360
+ */
361
+ seedCapabilities(capabilities: PrinterCapabilities | null, peripheralId?: string): void;
304
362
  clear(): void;
305
363
  get count(): number;
306
364
  }
package/dist/index.d.ts CHANGED
@@ -204,17 +204,51 @@ interface TestMessages {
204
204
  clear(): void;
205
205
  get count(): number;
206
206
  }
207
+ interface ReceiptItem {
208
+ name: string;
209
+ quantity: number;
210
+ unitPrice: number;
211
+ total: number;
212
+ note?: string;
213
+ }
214
+ type ReceiptAlign = "left" | "center" | "right";
215
+ type ReceiptTextSize = "small" | "normal" | "large";
216
+ /** Mirrors the edge SDK's block model so assertions can inspect the exact
217
+ * ordered content an app printed, not just its totals. */
218
+ type ReceiptBlock = {
219
+ type: "text";
220
+ text: string;
221
+ align?: ReceiptAlign;
222
+ bold?: boolean;
223
+ size?: ReceiptTextSize;
224
+ } | {
225
+ type: "columns";
226
+ left: string;
227
+ right: string;
228
+ bold?: boolean;
229
+ size?: ReceiptTextSize;
230
+ } | {
231
+ type: "separator";
232
+ style?: "dashed" | "solid";
233
+ } | {
234
+ type: "spacer";
235
+ lines?: number;
236
+ } | {
237
+ type: "items";
238
+ rows: ReceiptItem[];
239
+ } | {
240
+ type: "barcode";
241
+ format: "qr" | "code128";
242
+ data: string;
243
+ align?: ReceiptAlign;
244
+ };
207
245
  interface ReceiptPayload {
246
+ paperWidth?: "58mm" | "80mm";
247
+ blocks?: ReceiptBlock[];
208
248
  header?: {
209
249
  lines: string[];
210
250
  };
211
- items?: Array<{
212
- name: string;
213
- quantity: number;
214
- unitPrice: number;
215
- total: number;
216
- note?: string;
217
- }>;
251
+ items?: ReceiptItem[];
218
252
  subtotal?: number;
219
253
  tax?: number;
220
254
  discount?: number;
@@ -232,6 +266,23 @@ interface ReceiptPayload {
232
266
  };
233
267
  rawEscPos?: string;
234
268
  }
269
+ /** Mirrors the edge SDK's PrinterCapabilities so tests can drive
270
+ * `edge.getCapabilities()` without the SDK being a dependency here. */
271
+ interface PrinterCapabilities {
272
+ paperWidth: "58mm" | "80mm";
273
+ columns: number;
274
+ dotsPerLine: number;
275
+ renderMode: "text" | "raster";
276
+ textEncoding: "utf8" | "tis620";
277
+ unicodeText: boolean;
278
+ thaiText: boolean;
279
+ bold: boolean;
280
+ textSizes: ReceiptTextSize[];
281
+ barcodeFormats: Array<"qr" | "code128">;
282
+ rasterImages: boolean;
283
+ cut: boolean;
284
+ cashDrawer: boolean;
285
+ }
235
286
  interface DeviceInfo {
236
287
  id: string;
237
288
  displayName: string;
@@ -301,6 +352,13 @@ interface TestEdge {
301
352
  seedDevices(devices: DeviceInfo[]): void;
302
353
  seedPeripherals(peripherals: PeripheralInfo[]): void;
303
354
  seedBindings(bindings: BindingInfo[]): void;
355
+ /**
356
+ * Answer `edge.getCapabilities()` with a specific printer — e.g. a 58mm or a
357
+ * raster/Thai one — so layout and preview code can be tested against it.
358
+ * Pass a `peripheralId` to model stations with different printers; without
359
+ * one this sets the default every untargeted lookup falls back to.
360
+ */
361
+ seedCapabilities(capabilities: PrinterCapabilities | null, peripheralId?: string): void;
304
362
  clear(): void;
305
363
  get count(): number;
306
364
  }
package/dist/index.js CHANGED
@@ -117,6 +117,8 @@ var state = globalSingleton("state", () => ({
117
117
  edgeDevices: [],
118
118
  edgePeripherals: [],
119
119
  edgeBindings: /* @__PURE__ */ new Map(),
120
+ edgeCapabilities: null,
121
+ edgePeripheralCapabilities: /* @__PURE__ */ new Map(),
120
122
  allowNetwork: false
121
123
  }));
122
124
  function requireDb() {
@@ -1877,6 +1879,44 @@ function buildBinding(alias, peripheralId) {
1877
1879
  updatedAt: now2()
1878
1880
  };
1879
1881
  }
1882
+ var DEFAULT_CAPABILITIES = {
1883
+ paperWidth: "80mm",
1884
+ columns: 48,
1885
+ dotsPerLine: 576,
1886
+ renderMode: "text",
1887
+ textEncoding: "utf8",
1888
+ unicodeText: false,
1889
+ thaiText: false,
1890
+ bold: true,
1891
+ textSizes: ["small", "normal", "large"],
1892
+ barcodeFormats: ["qr"],
1893
+ rasterImages: true,
1894
+ cut: true,
1895
+ cashDrawer: false
1896
+ };
1897
+ function handleCapabilities(request) {
1898
+ const url = new URL(request.url);
1899
+ const alias = url.searchParams.get("alias");
1900
+ const deviceId = url.searchParams.get("deviceId");
1901
+ let peripheralId = url.searchParams.get("peripheralId");
1902
+ if (alias) {
1903
+ const binding = state.edgeBindings.get(alias);
1904
+ if (!binding) return bindingNotFound(alias);
1905
+ peripheralId = binding.peripheral?.id ?? null;
1906
+ }
1907
+ if (peripheralId) {
1908
+ const peripheral = findPeripheral(peripheralId);
1909
+ if (!peripheral) {
1910
+ return edgeFailure("Peripheral not found or its device is not granted to this project", 404);
1911
+ }
1912
+ const seeded = state.edgePeripheralCapabilities.get(peripheralId);
1913
+ return success({ capabilities: seeded ?? state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1914
+ }
1915
+ if (deviceId && !state.edgeDevices.some((d) => d.id === deviceId)) {
1916
+ return edgeFailure("Project does not have access to this device", 403);
1917
+ }
1918
+ return success({ capabilities: state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1919
+ }
1880
1920
  function handleListDevices() {
1881
1921
  return success({ devices: [...state.edgeDevices] });
1882
1922
  }
@@ -1980,6 +2020,9 @@ async function handleEdgeRequest(request, subPath) {
1980
2020
  if (subPath === "/print" && method === "POST") {
1981
2021
  return handlePrint(request);
1982
2022
  }
2023
+ if (subPath === "/capabilities" && method === "GET") {
2024
+ return handleCapabilities(request);
2025
+ }
1983
2026
  if (subPath === "/devices" && method === "GET") {
1984
2027
  return handleListDevices();
1985
2028
  }
@@ -2041,7 +2084,17 @@ function createEdge() {
2041
2084
  state.edgeBindings.set(binding.alias, binding);
2042
2085
  }
2043
2086
  },
2087
+ seedCapabilities(capabilities, peripheralId) {
2088
+ if (peripheralId === void 0) {
2089
+ state.edgeCapabilities = capabilities;
2090
+ return;
2091
+ }
2092
+ if (capabilities === null) state.edgePeripheralCapabilities.delete(peripheralId);
2093
+ else state.edgePeripheralCapabilities.set(peripheralId, capabilities);
2094
+ },
2044
2095
  clear() {
2096
+ state.edgeCapabilities = null;
2097
+ state.edgePeripheralCapabilities.clear();
2045
2098
  state.edgePrints = [];
2046
2099
  state.edgeDisplays = [];
2047
2100
  state.edgeTestPrints = [];
package/dist/index.mjs CHANGED
@@ -71,6 +71,8 @@ var state = globalSingleton("state", () => ({
71
71
  edgeDevices: [],
72
72
  edgePeripherals: [],
73
73
  edgeBindings: /* @__PURE__ */ new Map(),
74
+ edgeCapabilities: null,
75
+ edgePeripheralCapabilities: /* @__PURE__ */ new Map(),
74
76
  allowNetwork: false
75
77
  }));
76
78
  function requireDb() {
@@ -1831,6 +1833,44 @@ function buildBinding(alias, peripheralId) {
1831
1833
  updatedAt: now2()
1832
1834
  };
1833
1835
  }
1836
+ var DEFAULT_CAPABILITIES = {
1837
+ paperWidth: "80mm",
1838
+ columns: 48,
1839
+ dotsPerLine: 576,
1840
+ renderMode: "text",
1841
+ textEncoding: "utf8",
1842
+ unicodeText: false,
1843
+ thaiText: false,
1844
+ bold: true,
1845
+ textSizes: ["small", "normal", "large"],
1846
+ barcodeFormats: ["qr"],
1847
+ rasterImages: true,
1848
+ cut: true,
1849
+ cashDrawer: false
1850
+ };
1851
+ function handleCapabilities(request) {
1852
+ const url = new URL(request.url);
1853
+ const alias = url.searchParams.get("alias");
1854
+ const deviceId = url.searchParams.get("deviceId");
1855
+ let peripheralId = url.searchParams.get("peripheralId");
1856
+ if (alias) {
1857
+ const binding = state.edgeBindings.get(alias);
1858
+ if (!binding) return bindingNotFound(alias);
1859
+ peripheralId = binding.peripheral?.id ?? null;
1860
+ }
1861
+ if (peripheralId) {
1862
+ const peripheral = findPeripheral(peripheralId);
1863
+ if (!peripheral) {
1864
+ return edgeFailure("Peripheral not found or its device is not granted to this project", 404);
1865
+ }
1866
+ const seeded = state.edgePeripheralCapabilities.get(peripheralId);
1867
+ return success({ capabilities: seeded ?? state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1868
+ }
1869
+ if (deviceId && !state.edgeDevices.some((d) => d.id === deviceId)) {
1870
+ return edgeFailure("Project does not have access to this device", 403);
1871
+ }
1872
+ return success({ capabilities: state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1873
+ }
1834
1874
  function handleListDevices() {
1835
1875
  return success({ devices: [...state.edgeDevices] });
1836
1876
  }
@@ -1934,6 +1974,9 @@ async function handleEdgeRequest(request, subPath) {
1934
1974
  if (subPath === "/print" && method === "POST") {
1935
1975
  return handlePrint(request);
1936
1976
  }
1977
+ if (subPath === "/capabilities" && method === "GET") {
1978
+ return handleCapabilities(request);
1979
+ }
1937
1980
  if (subPath === "/devices" && method === "GET") {
1938
1981
  return handleListDevices();
1939
1982
  }
@@ -1995,7 +2038,17 @@ function createEdge() {
1995
2038
  state.edgeBindings.set(binding.alias, binding);
1996
2039
  }
1997
2040
  },
2041
+ seedCapabilities(capabilities, peripheralId) {
2042
+ if (peripheralId === void 0) {
2043
+ state.edgeCapabilities = capabilities;
2044
+ return;
2045
+ }
2046
+ if (capabilities === null) state.edgePeripheralCapabilities.delete(peripheralId);
2047
+ else state.edgePeripheralCapabilities.set(peripheralId, capabilities);
2048
+ },
1998
2049
  clear() {
2050
+ state.edgeCapabilities = null;
2051
+ state.edgePeripheralCapabilities.clear();
1999
2052
  state.edgePrints = [];
2000
2053
  state.edgeDisplays = [];
2001
2054
  state.edgeTestPrints = [];
@@ -63,6 +63,8 @@ var state = globalSingleton("state", () => ({
63
63
  edgeDevices: [],
64
64
  edgePeripherals: [],
65
65
  edgeBindings: /* @__PURE__ */ new Map(),
66
+ edgeCapabilities: null,
67
+ edgePeripheralCapabilities: /* @__PURE__ */ new Map(),
66
68
  allowNetwork: false
67
69
  }));
68
70
 
@@ -36,6 +36,8 @@ var state = globalSingleton("state", () => ({
36
36
  edgeDevices: [],
37
37
  edgePeripherals: [],
38
38
  edgeBindings: /* @__PURE__ */ new Map(),
39
+ edgeCapabilities: null,
40
+ edgePeripheralCapabilities: /* @__PURE__ */ new Map(),
39
41
  allowNetwork: false
40
42
  }));
41
43
 
package/dist/setup.js CHANGED
@@ -57,6 +57,8 @@ var state = globalSingleton("state", () => ({
57
57
  edgeDevices: [],
58
58
  edgePeripherals: [],
59
59
  edgeBindings: /* @__PURE__ */ new Map(),
60
+ edgeCapabilities: null,
61
+ edgePeripheralCapabilities: /* @__PURE__ */ new Map(),
60
62
  allowNetwork: false
61
63
  }));
62
64
  function requireDb() {
@@ -1473,6 +1475,44 @@ function buildBinding(alias, peripheralId) {
1473
1475
  updatedAt: now2()
1474
1476
  };
1475
1477
  }
1478
+ var DEFAULT_CAPABILITIES = {
1479
+ paperWidth: "80mm",
1480
+ columns: 48,
1481
+ dotsPerLine: 576,
1482
+ renderMode: "text",
1483
+ textEncoding: "utf8",
1484
+ unicodeText: false,
1485
+ thaiText: false,
1486
+ bold: true,
1487
+ textSizes: ["small", "normal", "large"],
1488
+ barcodeFormats: ["qr"],
1489
+ rasterImages: true,
1490
+ cut: true,
1491
+ cashDrawer: false
1492
+ };
1493
+ function handleCapabilities(request) {
1494
+ const url = new URL(request.url);
1495
+ const alias = url.searchParams.get("alias");
1496
+ const deviceId = url.searchParams.get("deviceId");
1497
+ let peripheralId = url.searchParams.get("peripheralId");
1498
+ if (alias) {
1499
+ const binding = state.edgeBindings.get(alias);
1500
+ if (!binding) return bindingNotFound(alias);
1501
+ peripheralId = binding.peripheral?.id ?? null;
1502
+ }
1503
+ if (peripheralId) {
1504
+ const peripheral = findPeripheral(peripheralId);
1505
+ if (!peripheral) {
1506
+ return edgeFailure("Peripheral not found or its device is not granted to this project", 404);
1507
+ }
1508
+ const seeded = state.edgePeripheralCapabilities.get(peripheralId);
1509
+ return success({ capabilities: seeded ?? state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1510
+ }
1511
+ if (deviceId && !state.edgeDevices.some((d) => d.id === deviceId)) {
1512
+ return edgeFailure("Project does not have access to this device", 403);
1513
+ }
1514
+ return success({ capabilities: state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1515
+ }
1476
1516
  function handleListDevices() {
1477
1517
  return success({ devices: [...state.edgeDevices] });
1478
1518
  }
@@ -1576,6 +1616,9 @@ async function handleEdgeRequest(request, subPath) {
1576
1616
  if (subPath === "/print" && method === "POST") {
1577
1617
  return handlePrint(request);
1578
1618
  }
1619
+ if (subPath === "/capabilities" && method === "GET") {
1620
+ return handleCapabilities(request);
1621
+ }
1579
1622
  if (subPath === "/devices" && method === "GET") {
1580
1623
  return handleListDevices();
1581
1624
  }
package/dist/setup.mjs CHANGED
@@ -33,6 +33,8 @@ var state = globalSingleton("state", () => ({
33
33
  edgeDevices: [],
34
34
  edgePeripherals: [],
35
35
  edgeBindings: /* @__PURE__ */ new Map(),
36
+ edgeCapabilities: null,
37
+ edgePeripheralCapabilities: /* @__PURE__ */ new Map(),
36
38
  allowNetwork: false
37
39
  }));
38
40
  function requireDb() {
@@ -1449,6 +1451,44 @@ function buildBinding(alias, peripheralId) {
1449
1451
  updatedAt: now2()
1450
1452
  };
1451
1453
  }
1454
+ var DEFAULT_CAPABILITIES = {
1455
+ paperWidth: "80mm",
1456
+ columns: 48,
1457
+ dotsPerLine: 576,
1458
+ renderMode: "text",
1459
+ textEncoding: "utf8",
1460
+ unicodeText: false,
1461
+ thaiText: false,
1462
+ bold: true,
1463
+ textSizes: ["small", "normal", "large"],
1464
+ barcodeFormats: ["qr"],
1465
+ rasterImages: true,
1466
+ cut: true,
1467
+ cashDrawer: false
1468
+ };
1469
+ function handleCapabilities(request) {
1470
+ const url = new URL(request.url);
1471
+ const alias = url.searchParams.get("alias");
1472
+ const deviceId = url.searchParams.get("deviceId");
1473
+ let peripheralId = url.searchParams.get("peripheralId");
1474
+ if (alias) {
1475
+ const binding = state.edgeBindings.get(alias);
1476
+ if (!binding) return bindingNotFound(alias);
1477
+ peripheralId = binding.peripheral?.id ?? null;
1478
+ }
1479
+ if (peripheralId) {
1480
+ const peripheral = findPeripheral(peripheralId);
1481
+ if (!peripheral) {
1482
+ return edgeFailure("Peripheral not found or its device is not granted to this project", 404);
1483
+ }
1484
+ const seeded = state.edgePeripheralCapabilities.get(peripheralId);
1485
+ return success({ capabilities: seeded ?? state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1486
+ }
1487
+ if (deviceId && !state.edgeDevices.some((d) => d.id === deviceId)) {
1488
+ return edgeFailure("Project does not have access to this device", 403);
1489
+ }
1490
+ return success({ capabilities: state.edgeCapabilities ?? DEFAULT_CAPABILITIES });
1491
+ }
1452
1492
  function handleListDevices() {
1453
1493
  return success({ devices: [...state.edgeDevices] });
1454
1494
  }
@@ -1552,6 +1592,9 @@ async function handleEdgeRequest(request, subPath) {
1552
1592
  if (subPath === "/print" && method === "POST") {
1553
1593
  return handlePrint(request);
1554
1594
  }
1595
+ if (subPath === "/capabilities" && method === "GET") {
1596
+ return handleCapabilities(request);
1597
+ }
1555
1598
  if (subPath === "/devices" && method === "GET") {
1556
1599
  return handleListDevices();
1557
1600
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stardeck-customer-apps/testing",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Vitest test harness for Stardeck customer apps — in-process Postgres (PGlite) plus a control-plane simulator so the real Stardeck SDKs run unmodified in tests",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -65,8 +65,8 @@
65
65
  "@electric-sql/pglite": "^0.3.0"
66
66
  },
67
67
  "peerDependencies": {
68
- "@stardeck-customer-apps/integrations-sdk": ">=1.6.0",
69
68
  "@stardeck-customer-apps/edge-sdk": ">=1.4.0",
69
+ "@stardeck-customer-apps/integrations-sdk": ">=1.6.0",
70
70
  "@stardeck-customer-apps/payments-sdk": ">=0.1.0",
71
71
  "@stardeck-customer-apps/storage-sdk": ">=0.1.0",
72
72
  "next": "^14.0.0 || ^15.0.0 || ^16.0.0",
@@ -93,8 +93,8 @@
93
93
  "@eslint/js": "^9.0.0",
94
94
  "@neondatabase/serverless": "^1.0.0",
95
95
  "@stardeck-customer-apps/data-store-sdk": "*",
96
- "@stardeck-customer-apps/email-sdk": "*",
97
96
  "@stardeck-customer-apps/edge-sdk": "*",
97
+ "@stardeck-customer-apps/email-sdk": "*",
98
98
  "@stardeck-customer-apps/integrations-sdk": "*",
99
99
  "@stardeck-customer-apps/payments-sdk": "*",
100
100
  "@stardeck-customer-apps/storage-sdk": "*",
@@ -102,7 +102,7 @@
102
102
  "@types/node": "^24.10.1",
103
103
  "kysely": "^0.27.0",
104
104
  "kysely-neon": "^2.0.0",
105
- "next": "^15.0.0",
105
+ "next": "^16.2.12",
106
106
  "tsup": "^8.0.0",
107
107
  "typescript": "^5.0.0",
108
108
  "typescript-eslint": "^8.0.0",