@kahitsan/plugin-sdk 0.4.4 → 0.5.0-staging.26
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/dist/index.d.ts +13 -1
- package/dist/index.js +1 -1
- package/dist/test.d.ts +32 -0
- package/dist/test.js +121 -0
- package/package.json +5 -1
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { QueryResultRow, QueryResult, PoolClient, Pool } from 'pg';
|
|
2
|
+
import { MiddlewareHandler } from 'hono';
|
|
3
|
+
interface FakePluginDb {
|
|
4
|
+
query<R extends QueryResultRow = QueryResultRow>(text: string, params?: readonly unknown[]): Promise<QueryResult<R>>;
|
|
5
|
+
connect(): Promise<PoolClient>;
|
|
6
|
+
}
|
|
7
|
+
interface RecordedQuery {
|
|
8
|
+
text: string;
|
|
9
|
+
params: readonly unknown[];
|
|
10
|
+
}
|
|
11
|
+
declare function fakePluginDb(responder?: QueryResultRow[] | ((text: string, params: readonly unknown[]) => QueryResultRow[])): FakePluginDb & {
|
|
12
|
+
calls: RecordedQuery[];
|
|
13
|
+
};
|
|
14
|
+
interface RollbackDb {
|
|
15
|
+
db: FakePluginDb;
|
|
16
|
+
rollback: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
declare function withRollbackDb(pool: Pool, schemas: readonly string[]): Promise<RollbackDb>;
|
|
19
|
+
interface StubIdentity {
|
|
20
|
+
workspaceId: number | string;
|
|
21
|
+
userId?: string;
|
|
22
|
+
role?: string;
|
|
23
|
+
wsRole?: string;
|
|
24
|
+
permissions?: string[];
|
|
25
|
+
}
|
|
26
|
+
declare function stubMiddleware(id: StubIdentity): {
|
|
27
|
+
requireAuth: MiddlewareHandler;
|
|
28
|
+
requireWorkspace: MiddlewareHandler;
|
|
29
|
+
requirePermission: (...codes: string[]) => MiddlewareHandler;
|
|
30
|
+
};
|
|
31
|
+
export { fakePluginDb, stubMiddleware, withRollbackDb };
|
|
32
|
+
export type { FakePluginDb, RecordedQuery, RollbackDb, StubIdentity };
|
package/dist/test.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// src/test/db.ts
|
|
2
|
+
function fakePluginDb(responder) {
|
|
3
|
+
const calls = [];
|
|
4
|
+
const resolveRows = typeof responder === "function" ? responder : () => Array.isArray(responder) ? responder : [];
|
|
5
|
+
const fakeClient = {
|
|
6
|
+
query: async (text, params) => {
|
|
7
|
+
calls.push({ text, params: params ?? [] });
|
|
8
|
+
return { rows: resolveRows(text, params ?? []) };
|
|
9
|
+
},
|
|
10
|
+
release: () => {
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
calls,
|
|
15
|
+
query: async (text, params) => {
|
|
16
|
+
calls.push({ text, params: params ?? [] });
|
|
17
|
+
return { rows: resolveRows(text, params ?? []) };
|
|
18
|
+
},
|
|
19
|
+
// Handlers that do `const c = await db.connect()` then `c.query(...)`
|
|
20
|
+
// share the same recorder so their SQL is captured too.
|
|
21
|
+
connect: async () => fakeClient
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function wrapWithSavepoints(client) {
|
|
25
|
+
let depth = 0;
|
|
26
|
+
const savepointName = () => `ksp_${depth}`;
|
|
27
|
+
const rewrite = (text) => {
|
|
28
|
+
const t = text.trim().toUpperCase();
|
|
29
|
+
if (t === "BEGIN" || t.startsWith("BEGIN") || t.startsWith("START TRANSACTION")) {
|
|
30
|
+
depth += 1;
|
|
31
|
+
return `SAVEPOINT ${savepointName()}`;
|
|
32
|
+
}
|
|
33
|
+
if (t === "COMMIT" || t.startsWith("COMMIT")) {
|
|
34
|
+
if (depth > 0) {
|
|
35
|
+
const name = savepointName();
|
|
36
|
+
depth -= 1;
|
|
37
|
+
return `RELEASE SAVEPOINT ${name}`;
|
|
38
|
+
}
|
|
39
|
+
return "RELEASE";
|
|
40
|
+
}
|
|
41
|
+
if (t === "ROLLBACK" || t.startsWith("ROLLBACK")) {
|
|
42
|
+
if (depth > 0) {
|
|
43
|
+
const name = savepointName();
|
|
44
|
+
depth -= 1;
|
|
45
|
+
return `ROLLBACK TO SAVEPOINT ${name}; RELEASE SAVEPOINT ${name}`;
|
|
46
|
+
}
|
|
47
|
+
return "ROLLBACK";
|
|
48
|
+
}
|
|
49
|
+
return text;
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
query: ((text, params) => client.query(rewrite(String(text)), params)),
|
|
53
|
+
release: () => {
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function withRollbackDb(pool, schemas) {
|
|
58
|
+
const realClient = await pool.connect();
|
|
59
|
+
await realClient.query("BEGIN");
|
|
60
|
+
const quoted = schemas.map((s) => `"${s.replace(/"/g, '""')}"`).join(", ");
|
|
61
|
+
await realClient.query(`SET search_path = ${quoted || '"public"'}, public`);
|
|
62
|
+
const sp = wrapWithSavepoints(realClient);
|
|
63
|
+
const fakePool = {
|
|
64
|
+
connect: async () => {
|
|
65
|
+
await realClient.query(`SET search_path = ${quoted || '"public"'}, public`);
|
|
66
|
+
return sp;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const db = {
|
|
70
|
+
query: async (text, params) => {
|
|
71
|
+
const c = await fakePool.connect();
|
|
72
|
+
return c.query(text, params);
|
|
73
|
+
},
|
|
74
|
+
connect: fakePool.connect
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
db,
|
|
78
|
+
rollback: async () => {
|
|
79
|
+
try {
|
|
80
|
+
await realClient.query("ROLLBACK");
|
|
81
|
+
} finally {
|
|
82
|
+
realClient.release();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/test/middleware.ts
|
|
89
|
+
function stubMiddleware(id) {
|
|
90
|
+
const apply = (c) => {
|
|
91
|
+
c.set("workspaceId", id.workspaceId);
|
|
92
|
+
c.set("wsRole", id.wsRole ?? "admin");
|
|
93
|
+
c.set("permissions", id.permissions ?? []);
|
|
94
|
+
c.set("user", {
|
|
95
|
+
id: id.userId ?? "test-user",
|
|
96
|
+
role: id.role ?? "admin"
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const requireAuth = async (c, next) => {
|
|
100
|
+
apply(c);
|
|
101
|
+
await next();
|
|
102
|
+
};
|
|
103
|
+
const requireWorkspace = async (c, next) => {
|
|
104
|
+
apply(c);
|
|
105
|
+
await next();
|
|
106
|
+
};
|
|
107
|
+
const requirePermission = (...codes) => async (c, next) => {
|
|
108
|
+
apply(c);
|
|
109
|
+
const granted = c.get("permissions") ?? [];
|
|
110
|
+
if (!codes.some((code) => granted.includes(code))) {
|
|
111
|
+
return c.json({ error: `Missing permission: ${codes.join(", ")}` }, 403);
|
|
112
|
+
}
|
|
113
|
+
await next();
|
|
114
|
+
};
|
|
115
|
+
return { requireAuth, requireWorkspace, requirePermission };
|
|
116
|
+
}
|
|
117
|
+
export {
|
|
118
|
+
fakePluginDb,
|
|
119
|
+
stubMiddleware,
|
|
120
|
+
withRollbackDb
|
|
121
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kahitsan/plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-staging.26",
|
|
4
4
|
"description": "THE single public author surface for Hilinga plugins: createPlugin, auth guards, the workspace-scoped data surface (makeDataSurface) + migrations + withTenantContext, cross-plugin RPC, and the wire-protocol verify surface. One npm package, no -composite/-protocol companions. Self-contained, no kernel internals, no signing capability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"./flow": {
|
|
14
14
|
"types": "./dist/flow-bundle.d.ts",
|
|
15
15
|
"import": "./dist/flow-bundle.js"
|
|
16
|
+
},
|
|
17
|
+
"./test": {
|
|
18
|
+
"types": "./dist/test.d.ts",
|
|
19
|
+
"import": "./dist/test.js"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"files": [
|