@iadev93/zuno-express 0.0.12 → 0.0.13
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 +11 -1
- package/dist/index.d.mts +19 -7
- package/dist/index.d.ts +19 -7
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -22,12 +22,18 @@ Peer dependency:
|
|
|
22
22
|
```ts
|
|
23
23
|
import express from "express";
|
|
24
24
|
import { createZunoExpress } from "@iadev93/zuno-express";
|
|
25
|
+
import { createZunoServerState } from "@iadev93/zuno/server";
|
|
25
26
|
|
|
26
27
|
const app = express();
|
|
27
28
|
app.use(express.json());
|
|
28
29
|
|
|
30
|
+
const server = createZunoServerState();
|
|
29
31
|
const zuno = createZunoExpress({
|
|
30
|
-
|
|
32
|
+
server,
|
|
33
|
+
authorize: ({ request, action }) => {
|
|
34
|
+
// Replace with your session, API-key, or tenant policy.
|
|
35
|
+
return Boolean(request.user) && (action === "read" || request.user.canWrite);
|
|
36
|
+
},
|
|
31
37
|
});
|
|
32
38
|
|
|
33
39
|
// Unified handlers
|
|
@@ -46,6 +52,10 @@ app.listen(3000);
|
|
|
46
52
|
|
|
47
53
|
Returns an object containing the following Express handlers:
|
|
48
54
|
|
|
55
|
+
Each call creates isolated server state by default. Pass `server` when several routers or custom endpoints must share the same authoritative universe. The returned object exposes the selected instance as `zuno.server`.
|
|
56
|
+
|
|
57
|
+
The optional `authorize` hook runs before SSE/snapshot reads and mutation writes. Returning `false` produces a `403 FORBIDDEN` response without reading or mutating server state.
|
|
58
|
+
|
|
49
59
|
#### `sse` (GET)
|
|
50
60
|
Handles persistent SSE connections, heartbeats, and initial synchronization.
|
|
51
61
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
import { ZunoServerState } from '@iadev93/zuno/server';
|
|
2
3
|
import { Request, Response } from 'express';
|
|
3
4
|
|
|
5
|
+
type ZunoExpressAuthorizationContext = {
|
|
6
|
+
action: "read" | "write";
|
|
7
|
+
request: Request;
|
|
8
|
+
event?: unknown;
|
|
9
|
+
};
|
|
4
10
|
/**
|
|
5
11
|
* Options for creating an Express router for Zuno.
|
|
6
12
|
*/
|
|
7
13
|
type CreateZunoExpressOptions = {
|
|
8
14
|
/** Optional custom headers to be sent with the SSE response. */
|
|
9
15
|
headers?: IncomingHttpHeaders;
|
|
16
|
+
/** Isolated authoritative server state. A new instance is created by default. */
|
|
17
|
+
server?: ZunoServerState;
|
|
18
|
+
/** Provider-agnostic authorization hook for snapshots, SSE, and mutations. */
|
|
19
|
+
authorize?: (context: ZunoExpressAuthorizationContext) => boolean | Promise<boolean>;
|
|
10
20
|
};
|
|
11
21
|
/**
|
|
12
22
|
* Creates a Zuno Express instance.
|
|
13
23
|
* Returns both granular handlers and a convenience `mount` helper.
|
|
14
24
|
*/
|
|
15
25
|
declare function createZunoExpress(opts?: CreateZunoExpressOptions): {
|
|
26
|
+
server: ZunoServerState;
|
|
16
27
|
/**
|
|
17
28
|
* Optional convenience method to mount all Zuno handlers at once.
|
|
18
29
|
* @param app The Express App or Router to mount the handlers on.
|
|
@@ -26,17 +37,17 @@ declare function createZunoExpress(opts?: CreateZunoExpressOptions): {
|
|
|
26
37
|
* SSE connection handler.
|
|
27
38
|
* Usage: app.get('/custom/sse', zuno.sse);
|
|
28
39
|
*/
|
|
29
|
-
sse: (req: Request, res: Response) => void
|
|
40
|
+
sse: (req: Request, res: Response) => Promise<void>;
|
|
30
41
|
/**
|
|
31
42
|
* Sync POST handler.
|
|
32
43
|
* Usage: app.post('/custom/sync', zuno.sync);
|
|
33
44
|
*/
|
|
34
|
-
sync: (req: Request, res: Response) => void
|
|
45
|
+
sync: (req: Request, res: Response) => Promise<void>;
|
|
35
46
|
/**
|
|
36
47
|
* Snapshot GET handler.
|
|
37
48
|
* Usage: app.get('/custom/snapshot', zuno.snapshot);
|
|
38
49
|
*/
|
|
39
|
-
snapshot: (req: Request, res: Response) => void
|
|
50
|
+
snapshot: (req: Request, res: Response) => Promise<void>;
|
|
40
51
|
};
|
|
41
52
|
/**
|
|
42
53
|
* A standalone helper to mount Zuno handlers on an Express app/router.
|
|
@@ -47,6 +58,7 @@ declare function mountZuno(app: {
|
|
|
47
58
|
}, opts?: CreateZunoExpressOptions & {
|
|
48
59
|
basePath?: string;
|
|
49
60
|
}): {
|
|
61
|
+
server: ZunoServerState;
|
|
50
62
|
/**
|
|
51
63
|
* Optional convenience method to mount all Zuno handlers at once.
|
|
52
64
|
* @param app The Express App or Router to mount the handlers on.
|
|
@@ -60,17 +72,17 @@ declare function mountZuno(app: {
|
|
|
60
72
|
* SSE connection handler.
|
|
61
73
|
* Usage: app.get('/custom/sse', zuno.sse);
|
|
62
74
|
*/
|
|
63
|
-
sse: (req: Request, res: Response) => void
|
|
75
|
+
sse: (req: Request, res: Response) => Promise<void>;
|
|
64
76
|
/**
|
|
65
77
|
* Sync POST handler.
|
|
66
78
|
* Usage: app.post('/custom/sync', zuno.sync);
|
|
67
79
|
*/
|
|
68
|
-
sync: (req: Request, res: Response) => void
|
|
80
|
+
sync: (req: Request, res: Response) => Promise<void>;
|
|
69
81
|
/**
|
|
70
82
|
* Snapshot GET handler.
|
|
71
83
|
* Usage: app.get('/custom/snapshot', zuno.snapshot);
|
|
72
84
|
*/
|
|
73
|
-
snapshot: (req: Request, res: Response) => void
|
|
85
|
+
snapshot: (req: Request, res: Response) => Promise<void>;
|
|
74
86
|
};
|
|
75
87
|
|
|
76
|
-
export { type CreateZunoExpressOptions, createZunoExpress, mountZuno };
|
|
88
|
+
export { type CreateZunoExpressOptions, type ZunoExpressAuthorizationContext, createZunoExpress, mountZuno };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { IncomingHttpHeaders } from 'node:http';
|
|
2
|
+
import { ZunoServerState } from '@iadev93/zuno/server';
|
|
2
3
|
import { Request, Response } from 'express';
|
|
3
4
|
|
|
5
|
+
type ZunoExpressAuthorizationContext = {
|
|
6
|
+
action: "read" | "write";
|
|
7
|
+
request: Request;
|
|
8
|
+
event?: unknown;
|
|
9
|
+
};
|
|
4
10
|
/**
|
|
5
11
|
* Options for creating an Express router for Zuno.
|
|
6
12
|
*/
|
|
7
13
|
type CreateZunoExpressOptions = {
|
|
8
14
|
/** Optional custom headers to be sent with the SSE response. */
|
|
9
15
|
headers?: IncomingHttpHeaders;
|
|
16
|
+
/** Isolated authoritative server state. A new instance is created by default. */
|
|
17
|
+
server?: ZunoServerState;
|
|
18
|
+
/** Provider-agnostic authorization hook for snapshots, SSE, and mutations. */
|
|
19
|
+
authorize?: (context: ZunoExpressAuthorizationContext) => boolean | Promise<boolean>;
|
|
10
20
|
};
|
|
11
21
|
/**
|
|
12
22
|
* Creates a Zuno Express instance.
|
|
13
23
|
* Returns both granular handlers and a convenience `mount` helper.
|
|
14
24
|
*/
|
|
15
25
|
declare function createZunoExpress(opts?: CreateZunoExpressOptions): {
|
|
26
|
+
server: ZunoServerState;
|
|
16
27
|
/**
|
|
17
28
|
* Optional convenience method to mount all Zuno handlers at once.
|
|
18
29
|
* @param app The Express App or Router to mount the handlers on.
|
|
@@ -26,17 +37,17 @@ declare function createZunoExpress(opts?: CreateZunoExpressOptions): {
|
|
|
26
37
|
* SSE connection handler.
|
|
27
38
|
* Usage: app.get('/custom/sse', zuno.sse);
|
|
28
39
|
*/
|
|
29
|
-
sse: (req: Request, res: Response) => void
|
|
40
|
+
sse: (req: Request, res: Response) => Promise<void>;
|
|
30
41
|
/**
|
|
31
42
|
* Sync POST handler.
|
|
32
43
|
* Usage: app.post('/custom/sync', zuno.sync);
|
|
33
44
|
*/
|
|
34
|
-
sync: (req: Request, res: Response) => void
|
|
45
|
+
sync: (req: Request, res: Response) => Promise<void>;
|
|
35
46
|
/**
|
|
36
47
|
* Snapshot GET handler.
|
|
37
48
|
* Usage: app.get('/custom/snapshot', zuno.snapshot);
|
|
38
49
|
*/
|
|
39
|
-
snapshot: (req: Request, res: Response) => void
|
|
50
|
+
snapshot: (req: Request, res: Response) => Promise<void>;
|
|
40
51
|
};
|
|
41
52
|
/**
|
|
42
53
|
* A standalone helper to mount Zuno handlers on an Express app/router.
|
|
@@ -47,6 +58,7 @@ declare function mountZuno(app: {
|
|
|
47
58
|
}, opts?: CreateZunoExpressOptions & {
|
|
48
59
|
basePath?: string;
|
|
49
60
|
}): {
|
|
61
|
+
server: ZunoServerState;
|
|
50
62
|
/**
|
|
51
63
|
* Optional convenience method to mount all Zuno handlers at once.
|
|
52
64
|
* @param app The Express App or Router to mount the handlers on.
|
|
@@ -60,17 +72,17 @@ declare function mountZuno(app: {
|
|
|
60
72
|
* SSE connection handler.
|
|
61
73
|
* Usage: app.get('/custom/sse', zuno.sse);
|
|
62
74
|
*/
|
|
63
|
-
sse: (req: Request, res: Response) => void
|
|
75
|
+
sse: (req: Request, res: Response) => Promise<void>;
|
|
64
76
|
/**
|
|
65
77
|
* Sync POST handler.
|
|
66
78
|
* Usage: app.post('/custom/sync', zuno.sync);
|
|
67
79
|
*/
|
|
68
|
-
sync: (req: Request, res: Response) => void
|
|
80
|
+
sync: (req: Request, res: Response) => Promise<void>;
|
|
69
81
|
/**
|
|
70
82
|
* Snapshot GET handler.
|
|
71
83
|
* Usage: app.get('/custom/snapshot', zuno.snapshot);
|
|
72
84
|
*/
|
|
73
|
-
snapshot: (req: Request, res: Response) => void
|
|
85
|
+
snapshot: (req: Request, res: Response) => Promise<void>;
|
|
74
86
|
};
|
|
75
87
|
|
|
76
|
-
export { type CreateZunoExpressOptions, createZunoExpress, mountZuno };
|
|
88
|
+
export { type CreateZunoExpressOptions, type ZunoExpressAuthorizationContext, createZunoExpress, mountZuno };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var server=require('@iadev93/zuno/server');function
|
|
1
|
+
'use strict';var server=require('@iadev93/zuno/server');function x(u){let{headers:i={},server:o=server.createZunoServerState(),authorize:r=()=>true}=u??{},s=e=>e.status(403).json({ok:false,reason:"FORBIDDEN"}),a={sse:async(e,n)=>{if(!await r({action:"read",request:e})){s(n);return}server.createSSEConnection(e,n,i,o);},sync:async(e,n)=>{let p=e.body;if(!await r({action:"write",request:e,event:p})){s(n);return}let t=server.applyStateEvent(p,o);if(!t.ok){n.status(t.reason==="VERSION_CONFLICT"?409:400).json({ok:false,reason:t.reason,...t.reason==="VERSION_CONFLICT"?{current:t.current}:{errors:t.errors}});return}n.status(200).json({ok:true,event:t.event});},snapshot:async(e,n)=>{if(!await r({action:"read",request:e})){s(n);return}server.sendSnapshot(e,n,o);}};return {...a,server:o,mount:(e,n="/zuno")=>{e.get(`${n}/sse`,a.sse),e.get(`${n}/snapshot`,a.snapshot),e.post(`${n}/sync`,a.sync);}}}function S(u,i){let{basePath:o="/zuno",...r}=i??{},s=x(r);return s.mount(u,o),s}exports.createZunoExpress=x;exports.mountZuno=S;//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["createZunoExpress","opts","headers","
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["createZunoExpress","opts","headers","server","createZunoServerState","authorize","forbidden","res","handlers","req","createSSEConnection","incoming","result","applyStateEvent","sendSnapshot","app","basePath","mountZuno","rest","zuno"],"mappings":"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","file":"index.js","sourcesContent":["import type { IncomingHttpHeaders } from \"node:http\";\nimport type { ZunoStateEvent } from \"@iadev93/zuno\";\nimport {\n\tapplyStateEvent,\n\tcreateSSEConnection,\n\tcreateZunoServerState,\n\tsendSnapshot,\n\ttype ZunoServerState,\n} from \"@iadev93/zuno/server\";\nimport type { Request, Response } from \"express\";\n\nexport type ZunoExpressAuthorizationContext = {\n\taction: \"read\" | \"write\";\n\trequest: Request;\n\tevent?: unknown;\n};\n\n/**\n * Options for creating an Express router for Zuno.\n */\nexport type CreateZunoExpressOptions = {\n\t/** Optional custom headers to be sent with the SSE response. */\n\theaders?: IncomingHttpHeaders;\n\t/** Isolated authoritative server state. A new instance is created by default. */\n\tserver?: ZunoServerState;\n\t/** Provider-agnostic authorization hook for snapshots, SSE, and mutations. */\n\tauthorize?: (\n\t\tcontext: ZunoExpressAuthorizationContext,\n\t) => boolean | Promise<boolean>;\n};\n\n/**\n * Creates a Zuno Express instance.\n * Returns both granular handlers and a convenience `mount` helper.\n */\nexport function createZunoExpress(opts?: CreateZunoExpressOptions) {\n\tconst {\n\t\theaders = {},\n\t\tserver = createZunoServerState(),\n\t\tauthorize = () => true,\n\t} = opts ?? {};\n\tconst forbidden = (res: Response) =>\n\t\tres.status(403).json({ ok: false, reason: \"FORBIDDEN\" });\n\n\t/**\n\t * Granular handlers for maximum control.\n\t * You can mount these manually to any route or wrap them in custom middleware.\n\t */\n\tconst handlers = {\n\t\t/**\n\t\t * SSE connection handler.\n\t\t * Usage: app.get('/custom/sse', zuno.sse);\n\t\t */\n\t\tsse: async (req: Request, res: Response) => {\n\t\t\tif (!(await authorize({ action: \"read\", request: req }))) {\n\t\t\t\tforbidden(res);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcreateSSEConnection(req, res, headers, server);\n\t\t},\n\n\t\t/**\n\t\t * Sync POST handler.\n\t\t * Usage: app.post('/custom/sync', zuno.sync);\n\t\t */\n\t\tsync: async (req: Request, res: Response) => {\n\t\t\tconst incoming = req.body as ZunoStateEvent;\n\t\t\tif (\n\t\t\t\t!(await authorize({ action: \"write\", request: req, event: incoming }))\n\t\t\t) {\n\t\t\t\tforbidden(res);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = applyStateEvent(incoming, server);\n\n\t\t\tif (!result.ok) {\n\t\t\t\tres.status(result.reason === \"VERSION_CONFLICT\" ? 409 : 400).json({\n\t\t\t\t\tok: false,\n\t\t\t\t\treason: result.reason,\n\t\t\t\t\t...(result.reason === \"VERSION_CONFLICT\"\n\t\t\t\t\t\t? { current: result.current }\n\t\t\t\t\t\t: { errors: result.errors }),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.status(200).json({ ok: true, event: result.event });\n\t\t},\n\n\t\t/**\n\t\t * Snapshot GET handler.\n\t\t * Usage: app.get('/custom/snapshot', zuno.snapshot);\n\t\t */\n\t\tsnapshot: async (req: Request, res: Response) => {\n\t\t\tif (!(await authorize({ action: \"read\", request: req }))) {\n\t\t\t\tforbidden(res);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsendSnapshot(req, res, server);\n\t\t},\n\t};\n\n\treturn {\n\t\t...handlers,\n\t\tserver,\n\t\t/**\n\t\t * Optional convenience method to mount all Zuno handlers at once.\n\t\t * @param app The Express App or Router to mount the handlers on.\n\t\t * @param basePath The base path for the Zuno routes (defaults to \"/zuno\").\n\t\t */\n\t\tmount: (\n\t\t\tapp: {\n\t\t\t\tget: (...args: unknown[]) => unknown;\n\t\t\t\tpost: (...args: unknown[]) => unknown;\n\t\t\t},\n\t\t\tbasePath = \"/zuno\",\n\t\t) => {\n\t\t\tapp.get(`${basePath}/sse`, handlers.sse);\n\t\t\tapp.get(`${basePath}/snapshot`, handlers.snapshot);\n\t\t\tapp.post(`${basePath}/sync`, handlers.sync);\n\t\t},\n\t};\n}\n\n/**\n * A standalone helper to mount Zuno handlers on an Express app/router.\n */\nexport function mountZuno(\n\tapp: {\n\t\tget: (...args: unknown[]) => unknown;\n\t\tpost: (...args: unknown[]) => unknown;\n\t},\n\topts?: CreateZunoExpressOptions & { basePath?: string },\n) {\n\tconst { basePath = \"/zuno\", ...rest } = opts ?? {};\n\tconst zuno = createZunoExpress(rest);\n\tzuno.mount(app, basePath);\n\treturn zuno;\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {sendSnapshot,createSSEConnection
|
|
1
|
+
import {createZunoServerState,sendSnapshot,applyStateEvent,createSSEConnection}from'@iadev93/zuno/server';function x(u){let{headers:i={},server:o=createZunoServerState(),authorize:r=()=>true}=u??{},s=e=>e.status(403).json({ok:false,reason:"FORBIDDEN"}),a={sse:async(e,n)=>{if(!await r({action:"read",request:e})){s(n);return}createSSEConnection(e,n,i,o);},sync:async(e,n)=>{let p=e.body;if(!await r({action:"write",request:e,event:p})){s(n);return}let t=applyStateEvent(p,o);if(!t.ok){n.status(t.reason==="VERSION_CONFLICT"?409:400).json({ok:false,reason:t.reason,...t.reason==="VERSION_CONFLICT"?{current:t.current}:{errors:t.errors}});return}n.status(200).json({ok:true,event:t.event});},snapshot:async(e,n)=>{if(!await r({action:"read",request:e})){s(n);return}sendSnapshot(e,n,o);}};return {...a,server:o,mount:(e,n="/zuno")=>{e.get(`${n}/sse`,a.sse),e.get(`${n}/snapshot`,a.snapshot),e.post(`${n}/sync`,a.sync);}}}function S(u,i){let{basePath:o="/zuno",...r}=i??{},s=x(r);return s.mount(u,o),s}export{x as createZunoExpress,S as mountZuno};//# sourceMappingURL=index.mjs.map
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["createZunoExpress","opts","headers","
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["createZunoExpress","opts","headers","server","createZunoServerState","authorize","forbidden","res","handlers","req","createSSEConnection","incoming","result","applyStateEvent","sendSnapshot","app","basePath","mountZuno","rest","zuno"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { IncomingHttpHeaders } from \"node:http\";\nimport type { ZunoStateEvent } from \"@iadev93/zuno\";\nimport {\n\tapplyStateEvent,\n\tcreateSSEConnection,\n\tcreateZunoServerState,\n\tsendSnapshot,\n\ttype ZunoServerState,\n} from \"@iadev93/zuno/server\";\nimport type { Request, Response } from \"express\";\n\nexport type ZunoExpressAuthorizationContext = {\n\taction: \"read\" | \"write\";\n\trequest: Request;\n\tevent?: unknown;\n};\n\n/**\n * Options for creating an Express router for Zuno.\n */\nexport type CreateZunoExpressOptions = {\n\t/** Optional custom headers to be sent with the SSE response. */\n\theaders?: IncomingHttpHeaders;\n\t/** Isolated authoritative server state. A new instance is created by default. */\n\tserver?: ZunoServerState;\n\t/** Provider-agnostic authorization hook for snapshots, SSE, and mutations. */\n\tauthorize?: (\n\t\tcontext: ZunoExpressAuthorizationContext,\n\t) => boolean | Promise<boolean>;\n};\n\n/**\n * Creates a Zuno Express instance.\n * Returns both granular handlers and a convenience `mount` helper.\n */\nexport function createZunoExpress(opts?: CreateZunoExpressOptions) {\n\tconst {\n\t\theaders = {},\n\t\tserver = createZunoServerState(),\n\t\tauthorize = () => true,\n\t} = opts ?? {};\n\tconst forbidden = (res: Response) =>\n\t\tres.status(403).json({ ok: false, reason: \"FORBIDDEN\" });\n\n\t/**\n\t * Granular handlers for maximum control.\n\t * You can mount these manually to any route or wrap them in custom middleware.\n\t */\n\tconst handlers = {\n\t\t/**\n\t\t * SSE connection handler.\n\t\t * Usage: app.get('/custom/sse', zuno.sse);\n\t\t */\n\t\tsse: async (req: Request, res: Response) => {\n\t\t\tif (!(await authorize({ action: \"read\", request: req }))) {\n\t\t\t\tforbidden(res);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcreateSSEConnection(req, res, headers, server);\n\t\t},\n\n\t\t/**\n\t\t * Sync POST handler.\n\t\t * Usage: app.post('/custom/sync', zuno.sync);\n\t\t */\n\t\tsync: async (req: Request, res: Response) => {\n\t\t\tconst incoming = req.body as ZunoStateEvent;\n\t\t\tif (\n\t\t\t\t!(await authorize({ action: \"write\", request: req, event: incoming }))\n\t\t\t) {\n\t\t\t\tforbidden(res);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = applyStateEvent(incoming, server);\n\n\t\t\tif (!result.ok) {\n\t\t\t\tres.status(result.reason === \"VERSION_CONFLICT\" ? 409 : 400).json({\n\t\t\t\t\tok: false,\n\t\t\t\t\treason: result.reason,\n\t\t\t\t\t...(result.reason === \"VERSION_CONFLICT\"\n\t\t\t\t\t\t? { current: result.current }\n\t\t\t\t\t\t: { errors: result.errors }),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.status(200).json({ ok: true, event: result.event });\n\t\t},\n\n\t\t/**\n\t\t * Snapshot GET handler.\n\t\t * Usage: app.get('/custom/snapshot', zuno.snapshot);\n\t\t */\n\t\tsnapshot: async (req: Request, res: Response) => {\n\t\t\tif (!(await authorize({ action: \"read\", request: req }))) {\n\t\t\t\tforbidden(res);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsendSnapshot(req, res, server);\n\t\t},\n\t};\n\n\treturn {\n\t\t...handlers,\n\t\tserver,\n\t\t/**\n\t\t * Optional convenience method to mount all Zuno handlers at once.\n\t\t * @param app The Express App or Router to mount the handlers on.\n\t\t * @param basePath The base path for the Zuno routes (defaults to \"/zuno\").\n\t\t */\n\t\tmount: (\n\t\t\tapp: {\n\t\t\t\tget: (...args: unknown[]) => unknown;\n\t\t\t\tpost: (...args: unknown[]) => unknown;\n\t\t\t},\n\t\t\tbasePath = \"/zuno\",\n\t\t) => {\n\t\t\tapp.get(`${basePath}/sse`, handlers.sse);\n\t\t\tapp.get(`${basePath}/snapshot`, handlers.snapshot);\n\t\t\tapp.post(`${basePath}/sync`, handlers.sync);\n\t\t},\n\t};\n}\n\n/**\n * A standalone helper to mount Zuno handlers on an Express app/router.\n */\nexport function mountZuno(\n\tapp: {\n\t\tget: (...args: unknown[]) => unknown;\n\t\tpost: (...args: unknown[]) => unknown;\n\t},\n\topts?: CreateZunoExpressOptions & { basePath?: string },\n) {\n\tconst { basePath = \"/zuno\", ...rest } = opts ?? {};\n\tconst zuno = createZunoExpress(rest);\n\tzuno.mount(app, basePath);\n\treturn zuno;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iadev93/zuno-express",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"express": "^5.0.0",
|
|
19
|
-
"@iadev93/zuno": "0.0.
|
|
19
|
+
"@iadev93/zuno": "0.0.11"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/express": "^5.0.0",
|
|
23
23
|
"@types/node": "^20.0.0",
|
|
24
24
|
"tsup": "^8.5.1",
|
|
25
25
|
"typescript": "^5.9.3",
|
|
26
|
-
"@iadev93/zuno": "0.0.
|
|
26
|
+
"@iadev93/zuno": "0.0.11"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"zuno",
|