@tessellatepos/plugin-sdk 0.0.0-stage → 0.0.1-alpha
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/lib/context-D0VJqR7Q.d.ts +49 -0
- package/lib/src/index.d.ts +144 -0
- package/lib/src/index.js +108 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/server/index.d.ts +42 -0
- package/lib/src/server/index.js +30 -0
- package/lib/src/server/index.js.map +1 -0
- package/package.json +44 -3
- package/README.md +0 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Order, Item } from '@tessellatepos/sdk';
|
|
2
|
+
|
|
3
|
+
declare enum PluginColumnType {
|
|
4
|
+
TEXT = "text",
|
|
5
|
+
VARCHAR = "varchar",
|
|
6
|
+
INTEGER = "integer",
|
|
7
|
+
BOOLEAN = "boolean",
|
|
8
|
+
TIMESTAMP = "timestamp",
|
|
9
|
+
JSONB = "jsonb"
|
|
10
|
+
}
|
|
11
|
+
interface PluginDatabaseColumn {
|
|
12
|
+
name: string;
|
|
13
|
+
type: PluginColumnType;
|
|
14
|
+
length?: number;
|
|
15
|
+
nullable?: boolean;
|
|
16
|
+
unique?: boolean;
|
|
17
|
+
auto_increment?: boolean;
|
|
18
|
+
default?: string | number | boolean | null;
|
|
19
|
+
primaryKey?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface PluginDatabaseTable {
|
|
22
|
+
name: string;
|
|
23
|
+
columns: PluginDatabaseColumn[];
|
|
24
|
+
}
|
|
25
|
+
interface PluginDatabaseMount {
|
|
26
|
+
tables: PluginDatabaseTable[];
|
|
27
|
+
}
|
|
28
|
+
interface PluginTableClient {
|
|
29
|
+
find(query?: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
30
|
+
findOne(id: string | number): Promise<Record<string, unknown> | null>;
|
|
31
|
+
insert(row: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
32
|
+
update(id: string | number, patch: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
33
|
+
delete(id: string | number): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
interface PluginDatabaseRepository {
|
|
36
|
+
table<Name extends string>(name: Name): PluginTableClient;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface PluginServerContext {
|
|
40
|
+
db: PluginDatabaseRepository;
|
|
41
|
+
session: {
|
|
42
|
+
deviceId: string;
|
|
43
|
+
locationId: string;
|
|
44
|
+
};
|
|
45
|
+
getOrder(orderId: string): Promise<Order | null>;
|
|
46
|
+
lookupItem(itemId: string): Promise<Item | null>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { type PluginServerContext as P, PluginColumnType as a, type PluginDatabaseColumn as b, type PluginDatabaseMount as c, type PluginDatabaseRepository as d, type PluginDatabaseTable as e, type PluginTableClient as f };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { CartItem, Discount, Order } from '@tessellatepos/sdk';
|
|
2
|
+
import { P as PluginServerContext } from '../context-D0VJqR7Q.js';
|
|
3
|
+
export { a as PluginColumnType, b as PluginDatabaseColumn, c as PluginDatabaseMount, d as PluginDatabaseRepository, e as PluginDatabaseTable, f as PluginTableClient } from '../context-D0VJqR7Q.js';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
interface PluginApiMountDefinition {
|
|
7
|
+
route: string;
|
|
8
|
+
method: PluginApiMethod;
|
|
9
|
+
handler: (req: PluginApiRequest, ctx: PluginServerContext) => Promise<PluginApiResponse> | PluginApiResponse;
|
|
10
|
+
}
|
|
11
|
+
declare enum PluginApiMethod {
|
|
12
|
+
GET = "GET",
|
|
13
|
+
POST = "POST",
|
|
14
|
+
PUT = "PUT",
|
|
15
|
+
DELETE = "DELETE"
|
|
16
|
+
}
|
|
17
|
+
interface PluginApiRequest {
|
|
18
|
+
params: Record<string, string>;
|
|
19
|
+
query: Record<string, string>;
|
|
20
|
+
body: unknown;
|
|
21
|
+
}
|
|
22
|
+
interface PluginApiResponse {
|
|
23
|
+
status?: number;
|
|
24
|
+
body: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface CartApi {
|
|
28
|
+
getItems(): CartItem[];
|
|
29
|
+
getTotal(): bigint;
|
|
30
|
+
addQuantity(item: CartItem): void;
|
|
31
|
+
removeQuantity(item: CartItem): void;
|
|
32
|
+
removeItem(item: CartItem): void;
|
|
33
|
+
clearCart(): void;
|
|
34
|
+
}
|
|
35
|
+
interface OrderApi {
|
|
36
|
+
readonly discounts: readonly Discount[];
|
|
37
|
+
completedOrder: Order | null;
|
|
38
|
+
}
|
|
39
|
+
interface SettingsApi {
|
|
40
|
+
locationId: string;
|
|
41
|
+
deviceId: string;
|
|
42
|
+
}
|
|
43
|
+
interface PluginApiClient {
|
|
44
|
+
call<T>(route: string, method: PluginApiMethod, params?: Record<string, string>, body?: unknown): Promise<T>;
|
|
45
|
+
}
|
|
46
|
+
interface PluginContext {
|
|
47
|
+
cart: CartApi;
|
|
48
|
+
order: OrderApi;
|
|
49
|
+
settings: SettingsApi;
|
|
50
|
+
navigate: (path: string) => void;
|
|
51
|
+
api: PluginApiClient;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare enum PluginPermission {
|
|
55
|
+
CART = "CART",
|
|
56
|
+
CATALOG = "CATALOG",
|
|
57
|
+
CHECKOUT = "CHECKOUT",
|
|
58
|
+
DEVICE = "DEVICE",
|
|
59
|
+
ORDER = "ORDER"
|
|
60
|
+
}
|
|
61
|
+
interface PluginPermissionProvider {
|
|
62
|
+
cart: boolean;
|
|
63
|
+
catalog: boolean;
|
|
64
|
+
checkout: boolean;
|
|
65
|
+
device: boolean;
|
|
66
|
+
order: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface PluginManifestAuthor {
|
|
70
|
+
name: string;
|
|
71
|
+
email?: string;
|
|
72
|
+
url?: string;
|
|
73
|
+
}
|
|
74
|
+
interface PluginManifestBugs {
|
|
75
|
+
url?: string;
|
|
76
|
+
email?: string;
|
|
77
|
+
}
|
|
78
|
+
interface PluginManifestFunding {
|
|
79
|
+
type?: string;
|
|
80
|
+
url: string;
|
|
81
|
+
}
|
|
82
|
+
interface PluginManifest {
|
|
83
|
+
name: string;
|
|
84
|
+
version: string;
|
|
85
|
+
description?: string;
|
|
86
|
+
keywords?: string[];
|
|
87
|
+
homepage?: string;
|
|
88
|
+
bugs?: string | PluginManifestBugs;
|
|
89
|
+
license?: string;
|
|
90
|
+
author?: string | PluginManifestAuthor;
|
|
91
|
+
contributors?: (string | PluginManifestAuthor)[];
|
|
92
|
+
maintainers?: (string | PluginManifestAuthor)[];
|
|
93
|
+
funding?: string | PluginManifestFunding | (string | PluginManifestFunding)[];
|
|
94
|
+
main: string;
|
|
95
|
+
engines?: {
|
|
96
|
+
tessellate?: string;
|
|
97
|
+
[engine: string]: string | undefined;
|
|
98
|
+
};
|
|
99
|
+
tessellate: {
|
|
100
|
+
permissions: PluginPermission[];
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface PluginUIMountDefinition {
|
|
105
|
+
mount: PluginUIMountPoint;
|
|
106
|
+
component: React.ComponentType<{
|
|
107
|
+
context: PluginContext;
|
|
108
|
+
}>;
|
|
109
|
+
}
|
|
110
|
+
declare enum PluginUIMountPoint {
|
|
111
|
+
PAGE = "page",
|
|
112
|
+
CART_SIDEBAR = "cart-sidebar",
|
|
113
|
+
CHECKOUT_FOOTER = "checkout-footer",
|
|
114
|
+
SETTINGS_TAB = "settings-tab",
|
|
115
|
+
RECEIPT_EXTRA = "receipt-extra"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Client-side plugin instance, mirroring the server-side Plugin class but
|
|
120
|
+
* scoped to UI mount registration only. The plugin's client entry's default
|
|
121
|
+
* export receives one of these and calls `register("ui", ...)` to declare
|
|
122
|
+
* which mount points its React components should render at.
|
|
123
|
+
*/
|
|
124
|
+
declare class FrontendPlugin {
|
|
125
|
+
private manifest;
|
|
126
|
+
private uiMounts;
|
|
127
|
+
constructor(manifest: PluginManifest);
|
|
128
|
+
register(kind: "ui", mount: PluginUIMountDefinition): void;
|
|
129
|
+
get(kind: "ui"): PluginUIMountDefinition[];
|
|
130
|
+
getManifest(): PluginManifest;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Creates a PluginApiClient scoped to a single plugin. All calls are
|
|
135
|
+
* routed to `/plugins/:pluginName/:route` on the backend, where the
|
|
136
|
+
* dispatcher only matches that plugin's own API mounts — a plugin
|
|
137
|
+
* cannot reach another plugin's routes.
|
|
138
|
+
*
|
|
139
|
+
* The route is sanitized to prevent path traversal (`..`) from escaping
|
|
140
|
+
* the plugin's namespace.
|
|
141
|
+
*/
|
|
142
|
+
declare function createPluginApiClient(backendUrl: string, pluginName: string): PluginApiClient;
|
|
143
|
+
|
|
144
|
+
export { type CartApi, FrontendPlugin, type OrderApi, type PluginApiClient, PluginApiMethod, type PluginApiMountDefinition, type PluginApiRequest, type PluginApiResponse, type PluginContext, type PluginManifest, type PluginManifestAuthor, type PluginManifestBugs, type PluginManifestFunding, PluginPermission, type PluginPermissionProvider, type PluginUIMountDefinition, PluginUIMountPoint, type SettingsApi, createPluginApiClient };
|
package/lib/src/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/client/frontendPlugin.ts
|
|
2
|
+
var FrontendPlugin = class {
|
|
3
|
+
constructor(manifest) {
|
|
4
|
+
this.uiMounts = [];
|
|
5
|
+
this.manifest = manifest;
|
|
6
|
+
}
|
|
7
|
+
register(kind, mount) {
|
|
8
|
+
if (kind !== "ui") return;
|
|
9
|
+
this.uiMounts.push(mount);
|
|
10
|
+
}
|
|
11
|
+
get(kind) {
|
|
12
|
+
if (kind !== "ui") return [];
|
|
13
|
+
return this.uiMounts;
|
|
14
|
+
}
|
|
15
|
+
getManifest() {
|
|
16
|
+
return this.manifest;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// src/client/pluginApiClient.ts
|
|
21
|
+
import { bigintReplacer } from "@tessellatepos/sdk";
|
|
22
|
+
function createPluginApiClient(backendUrl, pluginName) {
|
|
23
|
+
return {
|
|
24
|
+
async call(route, method, params, body) {
|
|
25
|
+
const normalizedRoute = route.startsWith("/") ? route : `/${route}`;
|
|
26
|
+
if (normalizedRoute.includes("..")) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Plugin API route contains illegal path segment: ${route}`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const url = new URL(
|
|
32
|
+
`/plugins/${pluginName}${normalizedRoute}`,
|
|
33
|
+
backendUrl
|
|
34
|
+
);
|
|
35
|
+
const expectedPrefix = `/plugins/${pluginName}/`;
|
|
36
|
+
if (!url.pathname.startsWith(expectedPrefix)) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Plugin API route escaped plugin namespace: ${route}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (params) {
|
|
42
|
+
for (const [key, value] of Object.entries(params)) {
|
|
43
|
+
url.searchParams.set(key, value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const res = await fetch(url, {
|
|
47
|
+
method,
|
|
48
|
+
headers: body !== void 0 ? { "Content-Type": "application/json" } : {},
|
|
49
|
+
body: body !== void 0 ? JSON.stringify(body, bigintReplacer) : void 0
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Plugin API call failed: ${method} ${route} \u2192 ${res.status}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return res.json();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/types/pluginApiMounts.ts
|
|
62
|
+
var PluginApiMethod = /* @__PURE__ */ ((PluginApiMethod2) => {
|
|
63
|
+
PluginApiMethod2["GET"] = "GET";
|
|
64
|
+
PluginApiMethod2["POST"] = "POST";
|
|
65
|
+
PluginApiMethod2["PUT"] = "PUT";
|
|
66
|
+
PluginApiMethod2["DELETE"] = "DELETE";
|
|
67
|
+
return PluginApiMethod2;
|
|
68
|
+
})(PluginApiMethod || {});
|
|
69
|
+
|
|
70
|
+
// src/types/pluginDatabaseMounts.ts
|
|
71
|
+
var PluginColumnType = /* @__PURE__ */ ((PluginColumnType2) => {
|
|
72
|
+
PluginColumnType2["TEXT"] = "text";
|
|
73
|
+
PluginColumnType2["VARCHAR"] = "varchar";
|
|
74
|
+
PluginColumnType2["INTEGER"] = "integer";
|
|
75
|
+
PluginColumnType2["BOOLEAN"] = "boolean";
|
|
76
|
+
PluginColumnType2["TIMESTAMP"] = "timestamp";
|
|
77
|
+
PluginColumnType2["JSONB"] = "jsonb";
|
|
78
|
+
return PluginColumnType2;
|
|
79
|
+
})(PluginColumnType || {});
|
|
80
|
+
|
|
81
|
+
// src/types/pluginPermissions.ts
|
|
82
|
+
var PluginPermission = /* @__PURE__ */ ((PluginPermission2) => {
|
|
83
|
+
PluginPermission2["CART"] = "CART";
|
|
84
|
+
PluginPermission2["CATALOG"] = "CATALOG";
|
|
85
|
+
PluginPermission2["CHECKOUT"] = "CHECKOUT";
|
|
86
|
+
PluginPermission2["DEVICE"] = "DEVICE";
|
|
87
|
+
PluginPermission2["ORDER"] = "ORDER";
|
|
88
|
+
return PluginPermission2;
|
|
89
|
+
})(PluginPermission || {});
|
|
90
|
+
|
|
91
|
+
// src/types/pluginUIMounts.ts
|
|
92
|
+
var PluginUIMountPoint = /* @__PURE__ */ ((PluginUIMountPoint2) => {
|
|
93
|
+
PluginUIMountPoint2["PAGE"] = "page";
|
|
94
|
+
PluginUIMountPoint2["CART_SIDEBAR"] = "cart-sidebar";
|
|
95
|
+
PluginUIMountPoint2["CHECKOUT_FOOTER"] = "checkout-footer";
|
|
96
|
+
PluginUIMountPoint2["SETTINGS_TAB"] = "settings-tab";
|
|
97
|
+
PluginUIMountPoint2["RECEIPT_EXTRA"] = "receipt-extra";
|
|
98
|
+
return PluginUIMountPoint2;
|
|
99
|
+
})(PluginUIMountPoint || {});
|
|
100
|
+
export {
|
|
101
|
+
FrontendPlugin,
|
|
102
|
+
PluginApiMethod,
|
|
103
|
+
PluginColumnType,
|
|
104
|
+
PluginPermission,
|
|
105
|
+
PluginUIMountPoint,
|
|
106
|
+
createPluginApiClient
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/frontendPlugin.ts","../../src/client/pluginApiClient.ts","../../src/types/pluginApiMounts.ts","../../src/types/pluginDatabaseMounts.ts","../../src/types/pluginPermissions.ts","../../src/types/pluginUIMounts.ts"],"sourcesContent":["import type { PluginManifest } from \"@/types/pluginManifest.ts\";\nimport type { PluginUIMountDefinition } from \"@/types/pluginUIMounts.ts\";\n\n/**\n * Client-side plugin instance, mirroring the server-side Plugin class but\n * scoped to UI mount registration only. The plugin's client entry's default\n * export receives one of these and calls `register(\"ui\", ...)` to declare\n * which mount points its React components should render at.\n */\nexport class FrontendPlugin {\n private manifest: PluginManifest;\n private uiMounts: PluginUIMountDefinition[] = [];\n\n constructor(manifest: PluginManifest) {\n this.manifest = manifest;\n }\n\n public register(kind: \"ui\", mount: PluginUIMountDefinition): void {\n if (kind !== \"ui\") return;\n this.uiMounts.push(mount);\n }\n\n public get(kind: \"ui\"): PluginUIMountDefinition[] {\n if (kind !== \"ui\") return [];\n return this.uiMounts;\n }\n\n public getManifest(): PluginManifest {\n return this.manifest;\n }\n}\n","import { bigintReplacer } from \"@tessellatepos/sdk\";\nimport type { PluginApiMethod } from \"@/types/pluginApiMounts.ts\";\nimport type { PluginApiClient } from \"@/client/context.ts\";\n\n/**\n * Creates a PluginApiClient scoped to a single plugin. All calls are\n * routed to `/plugins/:pluginName/:route` on the backend, where the\n * dispatcher only matches that plugin's own API mounts — a plugin\n * cannot reach another plugin's routes.\n *\n * The route is sanitized to prevent path traversal (`..`) from escaping\n * the plugin's namespace.\n */\nexport function createPluginApiClient(\n backendUrl: string,\n pluginName: string,\n): PluginApiClient {\n return {\n async call<T>(\n route: string,\n method: PluginApiMethod,\n params?: Record<string, string>,\n body?: unknown,\n ): Promise<T> {\n // Normalize route: ensure leading slash, reject path traversal\n const normalizedRoute = route.startsWith(\"/\") ? route : `/${route}`;\n if (normalizedRoute.includes(\"..\")) {\n throw new Error(\n `Plugin API route contains illegal path segment: ${route}`,\n );\n }\n\n const url = new URL(\n `/plugins/${pluginName}${normalizedRoute}`,\n backendUrl,\n );\n\n // Verify the resolved pathname is still within the plugin namespace\n const expectedPrefix = `/plugins/${pluginName}/`;\n if (!url.pathname.startsWith(expectedPrefix)) {\n throw new Error(\n `Plugin API route escaped plugin namespace: ${route}`,\n );\n }\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n }\n\n const res = await fetch(url, {\n method,\n headers:\n body !== undefined\n ? { \"Content-Type\": \"application/json\" }\n : {},\n body:\n body !== undefined\n ? JSON.stringify(body, bigintReplacer)\n : undefined,\n });\n\n if (!res.ok) {\n throw new Error(\n `Plugin API call failed: ${method} ${route} → ${res.status}`,\n );\n }\n\n // TODO: bigint fields in the response arrive as strings because\n // the backend serializes with bigintReplacer. Auto-revival without\n // type markers is unreliable, so callers must use parseMoney() or\n // BigInt() on amount fields as needed.\n return res.json() as Promise<T>;\n },\n };\n}\n","import type { PluginServerContext } from \"@/server/host/context.ts\";\n\nexport interface PluginApiMountDefinition {\n route: string;\n method: PluginApiMethod;\n handler: (\n req: PluginApiRequest,\n ctx: PluginServerContext,\n ) => Promise<PluginApiResponse> | PluginApiResponse;\n}\n\nexport enum PluginApiMethod {\n GET = \"GET\",\n POST = \"POST\",\n PUT = \"PUT\",\n DELETE = \"DELETE\",\n}\n\nexport interface PluginApiRequest {\n params: Record<string, string>;\n query: Record<string, string>;\n body: unknown;\n}\n\nexport interface PluginApiResponse {\n status?: number;\n body: unknown;\n}\n","export enum PluginColumnType {\n TEXT = \"text\",\n VARCHAR = \"varchar\",\n INTEGER = \"integer\",\n BOOLEAN = \"boolean\",\n TIMESTAMP = \"timestamp\",\n JSONB = \"jsonb\",\n}\n\nexport interface PluginDatabaseColumn {\n name: string;\n type: PluginColumnType;\n length?: number;\n nullable?: boolean;\n unique?: boolean;\n auto_increment?: boolean;\n default?: string | number | boolean | null;\n primaryKey?: boolean;\n}\n\nexport interface PluginDatabaseTable {\n name: string;\n columns: PluginDatabaseColumn[];\n}\n\nexport interface PluginDatabaseMount {\n tables: PluginDatabaseTable[];\n}\n\nexport interface PluginTableClient {\n find(query?: Record<string, unknown>): Promise<Record<string, unknown>[]>;\n findOne(id: string | number): Promise<Record<string, unknown> | null>;\n insert(row: Record<string, unknown>): Promise<Record<string, unknown>>;\n update(\n id: string | number,\n patch: Record<string, unknown>,\n ): Promise<Record<string, unknown>>;\n delete(id: string | number): Promise<void>;\n}\n\nexport interface PluginDatabaseRepository {\n table<Name extends string>(name: Name): PluginTableClient;\n}\n","export enum PluginPermission {\n CART = \"CART\",\n CATALOG = \"CATALOG\",\n CHECKOUT = \"CHECKOUT\",\n DEVICE = \"DEVICE\",\n ORDER = \"ORDER\",\n}\n\nexport interface PluginPermissionProvider {\n cart: boolean;\n catalog: boolean;\n checkout: boolean;\n device: boolean;\n order: boolean;\n}\n","import type React from \"react\";\nimport type { PluginContext } from \"@/client/context.ts\";\n\nexport interface PluginUIMountDefinition {\n mount: PluginUIMountPoint;\n component: React.ComponentType<{ context: PluginContext }>;\n}\n\nexport enum PluginUIMountPoint {\n PAGE = \"page\",\n CART_SIDEBAR = \"cart-sidebar\",\n CHECKOUT_FOOTER = \"checkout-footer\",\n SETTINGS_TAB = \"settings-tab\",\n RECEIPT_EXTRA = \"receipt-extra\",\n}\n"],"mappings":";AASO,IAAM,iBAAN,MAAqB;AAAA,EAIxB,YAAY,UAA0B;AAFtC,SAAQ,WAAsC,CAAC;AAG3C,SAAK,WAAW;AAAA,EACpB;AAAA,EAEO,SAAS,MAAY,OAAsC;AAC9D,QAAI,SAAS,KAAM;AACnB,SAAK,SAAS,KAAK,KAAK;AAAA,EAC5B;AAAA,EAEO,IAAI,MAAuC;AAC9C,QAAI,SAAS,KAAM,QAAO,CAAC;AAC3B,WAAO,KAAK;AAAA,EAChB;AAAA,EAEO,cAA8B;AACjC,WAAO,KAAK;AAAA,EAChB;AACJ;;;AC9BA,SAAS,sBAAsB;AAaxB,SAAS,sBACZ,YACA,YACe;AACf,SAAO;AAAA,IACH,MAAM,KACF,OACA,QACA,QACA,MACU;AAEV,YAAM,kBAAkB,MAAM,WAAW,GAAG,IAAI,QAAQ,IAAI,KAAK;AACjE,UAAI,gBAAgB,SAAS,IAAI,GAAG;AAChC,cAAM,IAAI;AAAA,UACN,mDAAmD,KAAK;AAAA,QAC5D;AAAA,MACJ;AAEA,YAAM,MAAM,IAAI;AAAA,QACZ,YAAY,UAAU,GAAG,eAAe;AAAA,QACxC;AAAA,MACJ;AAGA,YAAM,iBAAiB,YAAY,UAAU;AAC7C,UAAI,CAAC,IAAI,SAAS,WAAW,cAAc,GAAG;AAC1C,cAAM,IAAI;AAAA,UACN,8CAA8C,KAAK;AAAA,QACvD;AAAA,MACJ;AACA,UAAI,QAAQ;AACR,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,cAAI,aAAa,IAAI,KAAK,KAAK;AAAA,QACnC;AAAA,MACJ;AAEA,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QACzB;AAAA,QACA,SACI,SAAS,SACH,EAAE,gBAAgB,mBAAmB,IACrC,CAAC;AAAA,QACX,MACI,SAAS,SACH,KAAK,UAAU,MAAM,cAAc,IACnC;AAAA,MACd,CAAC;AAED,UAAI,CAAC,IAAI,IAAI;AACT,cAAM,IAAI;AAAA,UACN,2BAA2B,MAAM,IAAI,KAAK,WAAM,IAAI,MAAM;AAAA,QAC9D;AAAA,MACJ;AAMA,aAAO,IAAI,KAAK;AAAA,IACpB;AAAA,EACJ;AACJ;;;AChEO,IAAK,kBAAL,kBAAKA,qBAAL;AACH,EAAAA,iBAAA,SAAM;AACN,EAAAA,iBAAA,UAAO;AACP,EAAAA,iBAAA,SAAM;AACN,EAAAA,iBAAA,YAAS;AAJD,SAAAA;AAAA,GAAA;;;ACXL,IAAK,mBAAL,kBAAKC,sBAAL;AACH,EAAAA,kBAAA,UAAO;AACP,EAAAA,kBAAA,aAAU;AACV,EAAAA,kBAAA,aAAU;AACV,EAAAA,kBAAA,aAAU;AACV,EAAAA,kBAAA,eAAY;AACZ,EAAAA,kBAAA,WAAQ;AANA,SAAAA;AAAA,GAAA;;;ACAL,IAAK,mBAAL,kBAAKC,sBAAL;AACH,EAAAA,kBAAA,UAAO;AACP,EAAAA,kBAAA,aAAU;AACV,EAAAA,kBAAA,cAAW;AACX,EAAAA,kBAAA,YAAS;AACT,EAAAA,kBAAA,WAAQ;AALA,SAAAA;AAAA,GAAA;;;ACQL,IAAK,qBAAL,kBAAKC,wBAAL;AACH,EAAAA,oBAAA,UAAO;AACP,EAAAA,oBAAA,kBAAe;AACf,EAAAA,oBAAA,qBAAkB;AAClB,EAAAA,oBAAA,kBAAe;AACf,EAAAA,oBAAA,mBAAgB;AALR,SAAAA;AAAA,GAAA;","names":["PluginApiMethod","PluginColumnType","PluginPermission","PluginUIMountPoint"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export { P as PluginServerContext } from '../../context-D0VJqR7Q.js';
|
|
2
|
+
import { Order, TenderType, Money } from '@tessellatepos/sdk';
|
|
3
|
+
|
|
4
|
+
declare const hooks: {
|
|
5
|
+
beforeOrderCreate: HookDefinition<Order, Order>;
|
|
6
|
+
beforeCheckout: HookDefinition<{
|
|
7
|
+
orderId: string;
|
|
8
|
+
amount: Money;
|
|
9
|
+
}, void>;
|
|
10
|
+
beforePayment: HookDefinition<{
|
|
11
|
+
orderId: string;
|
|
12
|
+
method: TenderType;
|
|
13
|
+
}, void>;
|
|
14
|
+
};
|
|
15
|
+
declare const events: {
|
|
16
|
+
orderCreated: EventDefinition<(order: Order) => void>;
|
|
17
|
+
paymentCompleted: EventDefinition<(order: Order, method: TenderType) => void>;
|
|
18
|
+
orderCancelled: EventDefinition<(order: Order) => void>;
|
|
19
|
+
};
|
|
20
|
+
type HookResult<T> = {
|
|
21
|
+
ok: true;
|
|
22
|
+
value: T;
|
|
23
|
+
} | {
|
|
24
|
+
ok: false;
|
|
25
|
+
reason: string;
|
|
26
|
+
code?: string;
|
|
27
|
+
};
|
|
28
|
+
interface HookDefinition<TInput, TOutput = TInput> {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly kind: "hook";
|
|
31
|
+
readonly _input: TInput;
|
|
32
|
+
readonly _output: TOutput;
|
|
33
|
+
}
|
|
34
|
+
interface EventDefinition<THandler extends (...args: never[]) => void> {
|
|
35
|
+
readonly name: string;
|
|
36
|
+
readonly kind: "event";
|
|
37
|
+
readonly _handler: THandler;
|
|
38
|
+
}
|
|
39
|
+
declare function defineHook<TInput, TOutput = TInput>(name: string): HookDefinition<TInput, TOutput>;
|
|
40
|
+
declare function defineEvent<THandler extends (...args: never[]) => void>(name: string): EventDefinition<THandler>;
|
|
41
|
+
|
|
42
|
+
export { type EventDefinition, type HookDefinition, type HookResult, defineEvent, defineHook, events, hooks };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/server/host/hooks.ts
|
|
2
|
+
var hooks = {
|
|
3
|
+
beforeOrderCreate: defineHook("beforeOrderCreate"),
|
|
4
|
+
beforeCheckout: defineHook(
|
|
5
|
+
"beforeCheckout"
|
|
6
|
+
),
|
|
7
|
+
beforePayment: defineHook(
|
|
8
|
+
"beforePayment"
|
|
9
|
+
)
|
|
10
|
+
};
|
|
11
|
+
var events = {
|
|
12
|
+
orderCreated: defineEvent("orderCreated"),
|
|
13
|
+
paymentCompleted: defineEvent(
|
|
14
|
+
"paymentCompleted"
|
|
15
|
+
),
|
|
16
|
+
orderCancelled: defineEvent("orderCancelled")
|
|
17
|
+
};
|
|
18
|
+
function defineHook(name) {
|
|
19
|
+
return { name, kind: "hook" };
|
|
20
|
+
}
|
|
21
|
+
function defineEvent(name) {
|
|
22
|
+
return { name, kind: "event" };
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
defineEvent,
|
|
26
|
+
defineHook,
|
|
27
|
+
events,
|
|
28
|
+
hooks
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/server/host/hooks.ts"],"sourcesContent":["import type { Order, Money, TenderType } from \"@tessellatepos/sdk\";\n\nexport const hooks = {\n beforeOrderCreate: defineHook<Order>(\"beforeOrderCreate\"),\n beforeCheckout: defineHook<{ orderId: string; amount: Money }, void>(\n \"beforeCheckout\",\n ),\n beforePayment: defineHook<{ orderId: string; method: TenderType }, void>(\n \"beforePayment\",\n ),\n};\n\nexport const events = {\n orderCreated: defineEvent<(order: Order) => void>(\"orderCreated\"),\n paymentCompleted:\n defineEvent<(order: Order, method: TenderType) => void>(\n \"paymentCompleted\",\n ),\n orderCancelled: defineEvent<(order: Order) => void>(\"orderCancelled\"),\n};\n\nexport type HookResult<T> =\n { ok: true; value: T } | { ok: false; reason: string; code?: string };\n\nexport interface HookDefinition<TInput, TOutput = TInput> {\n readonly name: string;\n readonly kind: \"hook\";\n readonly _input: TInput;\n readonly _output: TOutput;\n}\n\nexport interface EventDefinition<THandler extends (...args: never[]) => void> {\n readonly name: string;\n readonly kind: \"event\";\n readonly _handler: THandler;\n}\n\nexport function defineHook<TInput, TOutput = TInput>(\n name: string,\n): HookDefinition<TInput, TOutput> {\n return { name, kind: \"hook\" } as HookDefinition<TInput, TOutput>;\n}\n\nexport function defineEvent<THandler extends (...args: never[]) => void>(\n name: string,\n): EventDefinition<THandler> {\n return { name, kind: \"event\" } as EventDefinition<THandler>;\n}\n"],"mappings":";AAEO,IAAM,QAAQ;AAAA,EACjB,mBAAmB,WAAkB,mBAAmB;AAAA,EACxD,gBAAgB;AAAA,IACZ;AAAA,EACJ;AAAA,EACA,eAAe;AAAA,IACX;AAAA,EACJ;AACJ;AAEO,IAAM,SAAS;AAAA,EAClB,cAAc,YAAoC,cAAc;AAAA,EAChE,kBACI;AAAA,IACI;AAAA,EACJ;AAAA,EACJ,gBAAgB,YAAoC,gBAAgB;AACxE;AAkBO,SAAS,WACZ,MAC+B;AAC/B,SAAO,EAAE,MAAM,MAAM,OAAO;AAChC;AAEO,SAAS,YACZ,MACyB;AACzB,SAAO,EAAE,MAAM,MAAM,QAAQ;AACjC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tessellatepos/plugin-sdk",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.1-alpha",
|
|
4
|
+
"description": "Curated contract surface for Tessellate POS plugins (client + server).",
|
|
5
|
+
"author": "Tessellate POS <tessellate@travisfriesen.ca>",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "lib/src/index.js",
|
|
9
|
+
"types": "lib/src/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./lib/src/index.d.ts",
|
|
13
|
+
"default": "./lib/src/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./server": {
|
|
16
|
+
"types": "./lib/src/server/index.d.ts",
|
|
17
|
+
"default": "./lib/src/server/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"lib"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@tessellatepos/sdk": "0.0.1-alpha",
|
|
25
|
+
"react": "^19.2.3"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@eslint/js": "^9.39.5",
|
|
29
|
+
"@types/node": "^24.10.9",
|
|
30
|
+
"@types/react": "^19.2.5",
|
|
31
|
+
"eslint": "^9.39.5",
|
|
32
|
+
"eslint-config-prettier": "^10.1.8",
|
|
33
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
34
|
+
"globals": "^17.11.0",
|
|
35
|
+
"jiti": "^2.6.1",
|
|
36
|
+
"prettier": "^3.9.6",
|
|
37
|
+
"tsup": "^8.5.1",
|
|
38
|
+
"typescript": "~5.9.3",
|
|
39
|
+
"typescript-eslint": "^8.68.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"lint": "prettier . && eslint .",
|
|
45
|
+
"lint:fix": "prettier . --write && eslint . --fix"
|
|
46
|
+
}
|
|
6
47
|
}
|
package/README.md
DELETED