@tablecraft/adapter-express 0.1.0-beta.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/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # @tablecraft/adapter-express
2
+
3
+ Express adapter for [TableCraft](https://github.com/your-org/tablecraft).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ bun add @tablecraft/engine @tablecraft/adapter-express
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Dynamic route
14
+
15
+ ```ts
16
+ import express from 'express';
17
+ import { createExpressMiddleware } from '@tablecraft/adapter-express';
18
+
19
+ const app = express();
20
+
21
+ app.get('/api/data/:table', createExpressMiddleware({
22
+ db,
23
+ schema,
24
+ configs,
25
+ getContext: (req) => ({
26
+ tenantId: req.headers['x-tenant-id'],
27
+ user: req.user, // from auth middleware
28
+ }),
29
+ }));
30
+ ```
31
+
32
+ ### Single table
33
+
34
+ ```ts
35
+ import { createExpressHandler } from '@tablecraft/adapter-express';
36
+
37
+ app.get('/api/users', createExpressHandler({ db, schema, config: usersConfig }));
38
+ ```
@@ -0,0 +1,31 @@
1
+ import type { Request, Response, NextFunction } from 'express';
2
+ import { TableConfig, ConfigInput, EngineContext } from '@tablecraft/engine';
3
+ export interface ExpressAdapterOptions {
4
+ db: unknown;
5
+ schema: Record<string, unknown>;
6
+ configs: ConfigInput[] | Record<string, ConfigInput>;
7
+ /**
8
+ * Extract context from the Express request.
9
+ * Typically reads from `req.user`, `req.headers`, etc.
10
+ */
11
+ getContext?: (req: Request) => EngineContext | Promise<EngineContext>;
12
+ /**
13
+ * Override built-in access check with your own logic.
14
+ */
15
+ checkAccess?: (config: TableConfig, context: EngineContext, req: Request) => boolean | Promise<boolean>;
16
+ }
17
+ /**
18
+ * Creates an Express middleware for dynamic `:table` routes.
19
+ */
20
+ export declare function createExpressMiddleware(options: ExpressAdapterOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
21
+ /**
22
+ * Creates a handler for a single table — no `:table` param needed.
23
+ */
24
+ export declare function createExpressHandler(options: {
25
+ db: unknown;
26
+ schema: Record<string, unknown>;
27
+ config: ConfigInput;
28
+ getContext?: (req: Request) => EngineContext | Promise<EngineContext>;
29
+ checkAccess?: (config: TableConfig, context: EngineContext, req: Request) => boolean | Promise<boolean>;
30
+ }): (req: Request, res: Response, next: NextFunction) => Promise<void>;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAML,WAAW,EACX,WAAW,EACX,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrD;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACtE;;OAEG;IACH,WAAW,CAAC,EAAE,CACZ,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,OAAO,KACT,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,qBAAqB,IAQlE,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CAyEjB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACtE,WAAW,CAAC,EAAE,CACZ,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,OAAO,KACT,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC,IAKG,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CAsCjB"}
package/dist/index.js ADDED
@@ -0,0 +1,113 @@
1
+ import { createEngines, createTableEngine, parseRequest, checkAccess as defaultCheckAccess, getExportMeta, } from '@tablecraft/engine';
2
+ /**
3
+ * Creates an Express middleware for dynamic `:table` routes.
4
+ */
5
+ export function createExpressMiddleware(options) {
6
+ const engines = createEngines({
7
+ db: options.db,
8
+ schema: options.schema,
9
+ configs: options.configs,
10
+ });
11
+ return async function tablecraftMiddleware(req, res, next) {
12
+ try {
13
+ const tableName = req.params.table;
14
+ if (!tableName) {
15
+ res.status(400).json({ error: 'Missing :table route parameter' });
16
+ return;
17
+ }
18
+ // ─── Metadata endpoint: GET /api/data/users/_meta ───
19
+ if (tableName.endsWith('/_meta') || tableName.endsWith('_meta')) {
20
+ const actualName = tableName.replace(/\/?_meta$/, '');
21
+ const engine = engines[actualName];
22
+ if (!engine) {
23
+ res.status(404).json({ error: `Unknown resource '${actualName}'` });
24
+ return;
25
+ }
26
+ const context = options.getContext
27
+ ? await options.getContext(req)
28
+ : {};
29
+ const metadata = engine.getMetadata(context);
30
+ res.json(metadata);
31
+ return;
32
+ }
33
+ const engine = engines[tableName];
34
+ if (!engine) {
35
+ res.status(404).json({ error: `Unknown resource '${tableName}'` });
36
+ return;
37
+ }
38
+ const config = engine.getConfig();
39
+ const context = options.getContext
40
+ ? await options.getContext(req)
41
+ : {};
42
+ const hasAccess = options.checkAccess
43
+ ? await options.checkAccess(config, context, req)
44
+ : defaultCheckAccess(config, context);
45
+ if (!hasAccess) {
46
+ res.status(403).json({ error: 'Forbidden' });
47
+ return;
48
+ }
49
+ const params = parseRequest(req.query);
50
+ if (params.export) {
51
+ const allowed = config.export?.formats ?? ['csv', 'json'];
52
+ const enabled = config.export?.enabled ?? true;
53
+ if (!enabled || !allowed.includes(params.export)) {
54
+ res.status(400).json({ error: `Export format '${params.export}' not allowed` });
55
+ return;
56
+ }
57
+ const body = await engine.exportData(params, context);
58
+ const { contentType, filename } = getExportMeta(tableName, params.export);
59
+ res.setHeader('Content-Type', contentType);
60
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
61
+ res.send(body);
62
+ return;
63
+ }
64
+ const result = await engine.query(params, context);
65
+ res.setHeader('X-Total-Count', String(result.meta.total));
66
+ res.json(result);
67
+ }
68
+ catch (err) {
69
+ next(err);
70
+ }
71
+ };
72
+ }
73
+ /**
74
+ * Creates a handler for a single table — no `:table` param needed.
75
+ */
76
+ export function createExpressHandler(options) {
77
+ const { db, schema, config, getContext, checkAccess } = options;
78
+ const engine = createTableEngine({ db, schema, config });
79
+ return async function handler(req, res, next) {
80
+ try {
81
+ const context = getContext ? await getContext(req) : {};
82
+ const actualConfig = engine.getConfig();
83
+ const hasAccess = checkAccess
84
+ ? await checkAccess(actualConfig, context, req)
85
+ : defaultCheckAccess(actualConfig, context);
86
+ if (!hasAccess) {
87
+ res.status(403).json({ error: 'Forbidden' });
88
+ return;
89
+ }
90
+ const params = parseRequest(req.query);
91
+ if (params.export) {
92
+ const allowed = actualConfig.export?.formats ?? ['csv', 'json'];
93
+ if (!(actualConfig.export?.enabled ?? true) || !allowed.includes(params.export)) {
94
+ res.status(400).json({ error: `Export format '${params.export}' not allowed` });
95
+ return;
96
+ }
97
+ const body = await engine.exportData(params, context);
98
+ const { contentType, filename } = getExportMeta(actualConfig.name, params.export);
99
+ res.setHeader('Content-Type', contentType);
100
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
101
+ res.send(body);
102
+ return;
103
+ }
104
+ const result = await engine.query(params, context);
105
+ res.setHeader('X-Total-Count', String(result.meta.total));
106
+ res.json(result);
107
+ }
108
+ catch (err) {
109
+ next(err);
110
+ }
111
+ };
112
+ }
113
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@tablecraft/adapter-express",
3
+ "version": "0.1.0-beta.1",
4
+ "description": "Express adapter for TableCraft",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "bun run --bun tsc",
19
+ "dev": "bun run --bun tsc --watch",
20
+ "test": "vitest",
21
+ "typecheck": "bun run --bun tsc --noEmit"
22
+ },
23
+ "peerDependencies": {
24
+ "@tablecraft/engine": ">=0.1.0-beta.1",
25
+ "express": ">=4.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@tablecraft/engine": "workspace:*",
29
+ "@types/express": "^5.0.0",
30
+ "@types/node": "^25.2.2",
31
+ "typescript": "^5.3.3",
32
+ "vitest": "^1.2.0"
33
+ },
34
+ "keywords": [
35
+ "tablecraft",
36
+ "express",
37
+ "adapter"
38
+ ],
39
+ "license": "MIT"
40
+ }