@theaiplatform/miniapp-sdk 0.2.0 → 0.2.2

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 CHANGED
@@ -40,6 +40,17 @@ requests through host consent instead of browser `fetch`, and
40
40
  secrets remain in the host vault and are injected only by the native request
41
41
  authority.
42
42
 
43
+ `sdk.printing` is an optional desktop-only receipt printer API. It accepts only
44
+ bounded semantic version-1 receipt rows with printable ASCII text and a stable job key; the host chooses
45
+ the machine-local printer/profile and owns wrapping, feed, cut, and OS spooler
46
+ submission. Packages must declare the exact descriptor effect
47
+ `{ "kind": "physical-output", "resources": ["receipt-printer"] }` and a
48
+ persisted `printing.receipt` action with reusable consent, `do` autonomy, and
49
+ consequential risk. Printer identity, raw ESC/POS, direct USB/network access,
50
+ and `window.print()` are not SDK capabilities. Job-key journaling suppresses
51
+ routine reconnect duplicates, but physical exactly-once printing is impossible;
52
+ every result therefore carries `physicalExactlyOnce: false`.
53
+
43
54
  ## Discovery metadata
44
55
 
45
56
  Descriptor-backed packages may assign up to three unique
package/dist/index.d.ts CHANGED
@@ -348,6 +348,8 @@ export declare type MiniAppPlatformApi = {
348
348
  http?: MiniAppHttpApi;
349
349
  /** Desktop host capability; feature-detect before use on portable targets. */
350
350
  credentials?: MiniAppCredentialsApi;
351
+ /** Desktop host capability; feature-detect before use on portable targets. */
352
+ printing?: MiniAppReceiptPrintingApi;
351
353
  /** Browser capabilities appear only when the selected target supports them. */
352
354
  auth?: MiniAppAuthApi;
353
355
  vfs?: MiniAppVfsApi;
@@ -412,6 +414,64 @@ export declare type MiniAppProvisionProjectChatResult = {
412
414
  worktreeBaseCommit?: string | null;
413
415
  };
414
416
 
417
+ /** Bounded semantic receipt rendered, wrapped, fed, and cut by the desktop host. */
418
+ export declare type MiniAppReceiptDocument = {
419
+ version: 1;
420
+ /** One to 200 rows; each text field is capped at 512 printable ASCII characters. */
421
+ lines: MiniAppReceiptLine[];
422
+ /** Defaults to 3 and is capped at 8. */
423
+ feedLines?: number;
424
+ /** Defaults to true. */
425
+ cut?: boolean;
426
+ };
427
+
428
+ /** One semantic receipt row. Raw printer commands are intentionally absent. */
429
+ export declare type MiniAppReceiptLine = {
430
+ kind: 'text';
431
+ text: string;
432
+ alignment?: MiniAppReceiptTextAlignment;
433
+ weight?: MiniAppReceiptTextWeight;
434
+ } | {
435
+ kind: 'key-value';
436
+ label: string;
437
+ value: string;
438
+ } | {
439
+ kind: 'divider';
440
+ } | {
441
+ kind: 'blank';
442
+ };
443
+
444
+ /** Abstract machine-local printer readiness; destination identity stays host-only. */
445
+ export declare type MiniAppReceiptPrinterStatus = {
446
+ availability: 'unconfigured' | 'ready' | 'unavailable';
447
+ profile: 'receipt-58mm' | 'receipt-80mm' | null;
448
+ };
449
+
450
+ /** Desktop-only silent receipt output through the host-configured OS spooler. */
451
+ export declare type MiniAppReceiptPrintingApi = {
452
+ getStatus(): MiniAppMaybePromise<MiniAppReceiptPrinterStatus>;
453
+ submit(options: MiniAppReceiptPrintOptions): MiniAppMaybePromise<MiniAppReceiptPrintResult>;
454
+ };
455
+
456
+ export declare type MiniAppReceiptPrintOptions = {
457
+ /**
458
+ * Stable caller key, capped at 128 characters. The host journals it within
459
+ * installation, workspace, and configured-destination scope.
460
+ */
461
+ jobKey: string;
462
+ document: MiniAppReceiptDocument;
463
+ };
464
+
465
+ export declare type MiniAppReceiptPrintResult = {
466
+ disposition: 'submitted' | 'duplicate-suppressed' | 'indeterminate';
467
+ /** Always false: spooler acknowledgement cannot prove physical exactly-once output. */
468
+ physicalExactlyOnce: false;
469
+ };
470
+
471
+ export declare type MiniAppReceiptTextAlignment = 'left' | 'center' | 'right';
472
+
473
+ export declare type MiniAppReceiptTextWeight = 'normal' | 'bold';
474
+
415
475
  export declare type MiniAppSpecialistApi = {
416
476
  joinToChannel(channelId: string, specialistId: string): MiniAppMaybePromise<string>;
417
477
  listWorkspace(workspaceId: string): MiniAppMaybePromise<MiniAppSpecialistSummary[]>;
@@ -358,6 +358,10 @@ const optionalSafeId = (name) => {
358
358
  }
359
359
  return value;
360
360
  };
361
+ const packageTarget = optionalSafeId('target');
362
+ if (packageTarget !== undefined && packageTarget !== 'desktop' && packageTarget !== 'mobile') {
363
+ throw new Error('Invalid federated surface package target.');
364
+ }
361
365
  const exactOrigin = (value) => {
362
366
  const origin = new URL(value).origin;
363
367
  if (origin === 'null') throw new Error('The TAP host origin must be concrete.');
@@ -453,6 +457,10 @@ const MAX_PRESENCE_JSON_BYTES = 16 * 1024;
453
457
  const MAX_HTTP_REQUEST_JSON_BYTES = 11 * 1024 * 1024;
454
458
  const MAX_HTTP_URL_CHARS = 64 * 1024;
455
459
  const MAX_CREDENTIAL_REFERENCE_CHARS = 512;
460
+ const MAX_RECEIPT_JOB_KEY_CHARS = 128;
461
+ const MAX_RECEIPT_LINES = 200;
462
+ const MAX_RECEIPT_LINE_CHARS = 512;
463
+ const MAX_RECEIPT_TOTAL_CHARS = 32768;
456
464
  const PRESENCE_EVENT = 'tap-miniapp-presence-event';
457
465
  const HOST_ACTION_TIMEOUT_MS = 4000;
458
466
  const pendingHostActions = new Map();
@@ -820,6 +828,95 @@ const credentials = Object.freeze({
820
828
  });
821
829
  },
822
830
  });
831
+ const checkedReceiptDocument = (document) => {
832
+ requireOptionsObject(document, 'app.printing.submit document');
833
+ if (document.version !== 1 || !Array.isArray(document.lines) ||
834
+ document.lines.length < 1 || document.lines.length > MAX_RECEIPT_LINES ||
835
+ Object.keys(document).some(
836
+ (key) => !new Set(['version', 'lines', 'feedLines', 'cut']).has(key),
837
+ ) ||
838
+ (document.feedLines !== undefined &&
839
+ (!Number.isInteger(document.feedLines) || document.feedLines < 0 || document.feedLines > 8)) ||
840
+ (document.cut !== undefined && typeof document.cut !== 'boolean')) {
841
+ throw new Error('app.printing.submit document is invalid.');
842
+ }
843
+ let totalChars = 0;
844
+ const text = (value) => {
845
+ if (typeof value !== 'string' || value.length > MAX_RECEIPT_LINE_CHARS ||
846
+ /[^\u0020-\u007e]/u.test(value)) {
847
+ throw new Error('app.printing.submit receipt text is invalid.');
848
+ }
849
+ totalChars += value.length;
850
+ };
851
+ for (const line of document.lines) {
852
+ requireOptionsObject(line, 'app.printing.submit receipt line');
853
+ const keys = Object.keys(line);
854
+ if (line.kind === 'text') {
855
+ text(line.text);
856
+ if ((line.alignment !== undefined && !new Set(['left', 'center', 'right']).has(line.alignment)) ||
857
+ (line.weight !== undefined && !new Set(['normal', 'bold']).has(line.weight)) ||
858
+ keys.some((key) => !new Set(['kind', 'text', 'alignment', 'weight']).has(key))) {
859
+ throw new Error('app.printing.submit text line is invalid.');
860
+ }
861
+ } else if (line.kind === 'key-value') {
862
+ text(line.label);
863
+ text(line.value);
864
+ if (keys.some((key) => !new Set(['kind', 'label', 'value']).has(key))) {
865
+ throw new Error('app.printing.submit key-value line is invalid.');
866
+ }
867
+ } else if ((line.kind !== 'divider' && line.kind !== 'blank') || keys.length !== 1) {
868
+ throw new Error('app.printing.submit receipt line is invalid.');
869
+ }
870
+ }
871
+ if (totalChars > MAX_RECEIPT_TOTAL_CHARS) {
872
+ throw new Error('app.printing.submit receipt text exceeds the allowed size.');
873
+ }
874
+ return document;
875
+ };
876
+ const printing = Object.freeze({
877
+ getStatus() {
878
+ return invokeSdkHostAction(
879
+ 'tap.platform.printing.receipt.status',
880
+ {},
881
+ 'app.printing.getStatus',
882
+ 30000,
883
+ ).then((value) => {
884
+ if (!value || typeof value !== 'object' || Array.isArray(value) ||
885
+ !new Set(['unconfigured', 'ready', 'unavailable']).has(value.availability) ||
886
+ !new Set([null, 'receipt-58mm', 'receipt-80mm']).has(value.profile)) {
887
+ throw new Error('app.printing.getStatus returned an invalid result.');
888
+ }
889
+ return projectResultFields(value, ['availability', 'profile'], 'app.printing.getStatus');
890
+ });
891
+ },
892
+ submit(options) {
893
+ requireOptionsObject(options, 'app.printing.submit');
894
+ if (Object.keys(options).some((key) => !new Set(['jobKey', 'document']).has(key)) ||
895
+ !validPlatformPartition(options.jobKey, MAX_RECEIPT_JOB_KEY_CHARS)) {
896
+ throw new Error('app.printing.submit jobKey is invalid.');
897
+ }
898
+ checkedReceiptDocument(options.document);
899
+ return invokeSdkHostAction(
900
+ 'tap.platform.printing.receipt.submit',
901
+ options,
902
+ 'app.printing.submit',
903
+ // Physical output cannot be cancelled when an SDK timer expires. Keep
904
+ // the request correlated until the host reports its journaled outcome.
905
+ null,
906
+ ).then((value) => {
907
+ if (!value || typeof value !== 'object' || Array.isArray(value) ||
908
+ !new Set(['submitted', 'duplicate-suppressed', 'indeterminate']).has(value.disposition) ||
909
+ value.physicalExactlyOnce !== false) {
910
+ throw new Error('app.printing.submit returned an invalid result.');
911
+ }
912
+ return projectResultFields(
913
+ value,
914
+ ['disposition', 'physicalExactlyOnce'],
915
+ 'app.printing.submit',
916
+ );
917
+ });
918
+ },
919
+ });
823
920
  const channels = Object.freeze({
824
921
  create(options) {
825
922
  requireOptionString(options?.name, 'app.channels.create name');
@@ -1098,6 +1195,7 @@ const miniappSdk = Object.freeze({
1098
1195
  presence,
1099
1196
  http,
1100
1197
  credentials,
1198
+ ...(packageTarget === 'desktop' ? { printing } : {}),
1101
1199
  auth: platformAuth,
1102
1200
  vfs: platformVfs,
1103
1201
  specialist: platformSpecialist,
package/dist/sdk.d.ts CHANGED
@@ -319,6 +319,8 @@ export declare type MiniAppPlatformApi = {
319
319
  http?: MiniAppHttpApi;
320
320
  /** Desktop host capability; feature-detect before use on portable targets. */
321
321
  credentials?: MiniAppCredentialsApi;
322
+ /** Desktop host capability; feature-detect before use on portable targets. */
323
+ printing?: MiniAppReceiptPrintingApi;
322
324
  /** Browser capabilities appear only when the selected target supports them. */
323
325
  auth?: MiniAppAuthApi;
324
326
  vfs?: MiniAppVfsApi;
@@ -383,6 +385,64 @@ export declare type MiniAppProvisionProjectChatResult = {
383
385
  worktreeBaseCommit?: string | null;
384
386
  };
385
387
 
388
+ /** Bounded semantic receipt rendered, wrapped, fed, and cut by the desktop host. */
389
+ export declare type MiniAppReceiptDocument = {
390
+ version: 1;
391
+ /** One to 200 rows; each text field is capped at 512 printable ASCII characters. */
392
+ lines: MiniAppReceiptLine[];
393
+ /** Defaults to 3 and is capped at 8. */
394
+ feedLines?: number;
395
+ /** Defaults to true. */
396
+ cut?: boolean;
397
+ };
398
+
399
+ /** One semantic receipt row. Raw printer commands are intentionally absent. */
400
+ export declare type MiniAppReceiptLine = {
401
+ kind: 'text';
402
+ text: string;
403
+ alignment?: MiniAppReceiptTextAlignment;
404
+ weight?: MiniAppReceiptTextWeight;
405
+ } | {
406
+ kind: 'key-value';
407
+ label: string;
408
+ value: string;
409
+ } | {
410
+ kind: 'divider';
411
+ } | {
412
+ kind: 'blank';
413
+ };
414
+
415
+ /** Abstract machine-local printer readiness; destination identity stays host-only. */
416
+ export declare type MiniAppReceiptPrinterStatus = {
417
+ availability: 'unconfigured' | 'ready' | 'unavailable';
418
+ profile: 'receipt-58mm' | 'receipt-80mm' | null;
419
+ };
420
+
421
+ /** Desktop-only silent receipt output through the host-configured OS spooler. */
422
+ export declare type MiniAppReceiptPrintingApi = {
423
+ getStatus(): MiniAppMaybePromise<MiniAppReceiptPrinterStatus>;
424
+ submit(options: MiniAppReceiptPrintOptions): MiniAppMaybePromise<MiniAppReceiptPrintResult>;
425
+ };
426
+
427
+ export declare type MiniAppReceiptPrintOptions = {
428
+ /**
429
+ * Stable caller key, capped at 128 characters. The host journals it within
430
+ * installation, workspace, and configured-destination scope.
431
+ */
432
+ jobKey: string;
433
+ document: MiniAppReceiptDocument;
434
+ };
435
+
436
+ export declare type MiniAppReceiptPrintResult = {
437
+ disposition: 'submitted' | 'duplicate-suppressed' | 'indeterminate';
438
+ /** Always false: spooler acknowledgement cannot prove physical exactly-once output. */
439
+ physicalExactlyOnce: false;
440
+ };
441
+
442
+ export declare type MiniAppReceiptTextAlignment = 'left' | 'center' | 'right';
443
+
444
+ export declare type MiniAppReceiptTextWeight = 'normal' | 'bold';
445
+
386
446
  export declare type MiniAppSpecialistApi = {
387
447
  joinToChannel(channelId: string, specialistId: string): MiniAppMaybePromise<string>;
388
448
  listWorkspace(workspaceId: string): MiniAppMaybePromise<MiniAppSpecialistSummary[]>;
package/dist/web.d.ts CHANGED
@@ -360,6 +360,8 @@ export declare type MiniAppPlatformApi = {
360
360
  http?: MiniAppHttpApi;
361
361
  /** Desktop host capability; feature-detect before use on portable targets. */
362
362
  credentials?: MiniAppCredentialsApi;
363
+ /** Desktop host capability; feature-detect before use on portable targets. */
364
+ printing?: MiniAppReceiptPrintingApi;
363
365
  /** Browser capabilities appear only when the selected target supports them. */
364
366
  auth?: MiniAppAuthApi;
365
367
  vfs?: MiniAppVfsApi;
@@ -424,6 +426,64 @@ declare type MiniAppProvisionProjectChatResult = {
424
426
  worktreeBaseCommit?: string | null;
425
427
  };
426
428
 
429
+ /** Bounded semantic receipt rendered, wrapped, fed, and cut by the desktop host. */
430
+ declare type MiniAppReceiptDocument = {
431
+ version: 1;
432
+ /** One to 200 rows; each text field is capped at 512 printable ASCII characters. */
433
+ lines: MiniAppReceiptLine[];
434
+ /** Defaults to 3 and is capped at 8. */
435
+ feedLines?: number;
436
+ /** Defaults to true. */
437
+ cut?: boolean;
438
+ };
439
+
440
+ /** One semantic receipt row. Raw printer commands are intentionally absent. */
441
+ declare type MiniAppReceiptLine = {
442
+ kind: 'text';
443
+ text: string;
444
+ alignment?: MiniAppReceiptTextAlignment;
445
+ weight?: MiniAppReceiptTextWeight;
446
+ } | {
447
+ kind: 'key-value';
448
+ label: string;
449
+ value: string;
450
+ } | {
451
+ kind: 'divider';
452
+ } | {
453
+ kind: 'blank';
454
+ };
455
+
456
+ /** Abstract machine-local printer readiness; destination identity stays host-only. */
457
+ declare type MiniAppReceiptPrinterStatus = {
458
+ availability: 'unconfigured' | 'ready' | 'unavailable';
459
+ profile: 'receipt-58mm' | 'receipt-80mm' | null;
460
+ };
461
+
462
+ /** Desktop-only silent receipt output through the host-configured OS spooler. */
463
+ declare type MiniAppReceiptPrintingApi = {
464
+ getStatus(): MiniAppMaybePromise<MiniAppReceiptPrinterStatus>;
465
+ submit(options: MiniAppReceiptPrintOptions): MiniAppMaybePromise<MiniAppReceiptPrintResult>;
466
+ };
467
+
468
+ declare type MiniAppReceiptPrintOptions = {
469
+ /**
470
+ * Stable caller key, capped at 128 characters. The host journals it within
471
+ * installation, workspace, and configured-destination scope.
472
+ */
473
+ jobKey: string;
474
+ document: MiniAppReceiptDocument;
475
+ };
476
+
477
+ declare type MiniAppReceiptPrintResult = {
478
+ disposition: 'submitted' | 'duplicate-suppressed' | 'indeterminate';
479
+ /** Always false: spooler acknowledgement cannot prove physical exactly-once output. */
480
+ physicalExactlyOnce: false;
481
+ };
482
+
483
+ declare type MiniAppReceiptTextAlignment = 'left' | 'center' | 'right';
484
+
485
+ declare type MiniAppReceiptTextWeight = 'normal' | 'bold';
486
+
427
487
  declare type MiniAppSpecialistApi = {
428
488
  joinToChannel(channelId: string, specialistId: string): MiniAppMaybePromise<string>;
429
489
  listWorkspace(workspaceId: string): MiniAppMaybePromise<MiniAppSpecialistSummary[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theaiplatform/miniapp-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Public SDK for building portable miniapps that run in The AI Platform.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -67,9 +67,60 @@
67
67
  "LICENSE.md",
68
68
  "THIRD_PARTY_NOTICES.md"
69
69
  ],
70
+ "scripts": {
71
+ "prepack": "pnpm run build",
72
+ "build": "rm -rf dist && rslib build && node scripts/copy-rspack-loaders.mjs && node scripts/build-ui-styles.mjs",
73
+ "test": "rstest run",
74
+ "typecheck": "tsc --noEmit"
75
+ },
70
76
  "publishConfig": {
71
77
  "registry": "https://registry.npmjs.org",
72
- "access": "public"
78
+ "access": "public",
79
+ "main": "./dist/index.js",
80
+ "module": "./dist/index.js",
81
+ "types": "./dist/index.d.ts",
82
+ "exports": {
83
+ ".": {
84
+ "types": "./dist/index.d.ts",
85
+ "import": "./dist/index.js"
86
+ },
87
+ "./sdk": {
88
+ "types": "./dist/sdk.d.ts",
89
+ "import": "./dist/sdk.js"
90
+ },
91
+ "./mcp": {
92
+ "types": "./dist/mcp.d.ts",
93
+ "import": "./dist/mcp.js"
94
+ },
95
+ "./web": {
96
+ "types": "./dist/web.d.ts",
97
+ "import": "./dist/web.js"
98
+ },
99
+ "./ui": {
100
+ "types": "./dist/ui.d.ts",
101
+ "import": "./dist/ui.js"
102
+ },
103
+ "./ui/wasm": {
104
+ "types": "./dist/ui/wasm.d.ts",
105
+ "import": "./dist/ui/wasm.js"
106
+ },
107
+ "./ui/styles.css": "./dist/ui/styles.css",
108
+ "./ui/tailwind.css": "./dist/ui/tailwind.css",
109
+ "./ui-components.json": "./ui-components.json",
110
+ "./surface": {
111
+ "types": "./dist/surface.d.ts",
112
+ "import": "./dist/surface.js"
113
+ },
114
+ "./config": {
115
+ "types": "./dist/config.d.ts",
116
+ "import": "./dist/config.js"
117
+ },
118
+ "./rspack": {
119
+ "types": "./dist/rspack/index.d.ts",
120
+ "import": "./dist/rspack/index.js"
121
+ },
122
+ "./config-schema.json": "./config-schema.json"
123
+ }
73
124
  },
74
125
  "peerDependencies": {
75
126
  "@module-federation/rsbuild-plugin": "2.8.0",
@@ -113,17 +164,17 @@
113
164
  "@module-federation/runtime-tools": "2.8.0",
114
165
  "@rsbuild/core": "^2.1.4",
115
166
  "@rslib/core": "^0.23.2",
116
- "@rstest/core": "^0.9.10",
167
+ "@rstest/core": "catalog:rstest",
117
168
  "@tailwindcss/postcss": "^4.3.0",
118
169
  "@types/jsdom": "^28.0.3",
119
- "@types/node": "^25.7.0",
120
- "@types/react": "19.2.14",
121
- "@types/react-dom": "19.2.3",
170
+ "@types/node": "catalog:node",
171
+ "@types/react": "catalog:react",
172
+ "@types/react-dom": "catalog:react",
122
173
  "jsdom": "^29.1.1",
123
174
  "postcss": "^8.5.6",
124
175
  "tailwindcss": "^4.3.0",
125
176
  "tw-animate-css": "^1.4.0",
126
- "typescript": "npm:@typescript/typescript6@6.0.2"
177
+ "typescript": "catalog:typescript"
127
178
  },
128
179
  "dependencies": {
129
180
  "@radix-ui/react-alert-dialog": "^1.1.15",
@@ -152,11 +203,5 @@
152
203
  "saxes": "^6.0.0",
153
204
  "tailwind-merge": "^3.6.0",
154
205
  "zod": "^4.4.3"
155
- },
156
- "scripts": {
157
- "build": "rm -rf dist && rslib build && node scripts/copy-rspack-loaders.mjs && node scripts/build-ui-styles.mjs",
158
- "test": "rstest run",
159
- "typecheck": "tsc --noEmit"
160
- },
161
- "module": "./dist/index.js"
162
- }
206
+ }
207
+ }