@xlock/node 0.1.0

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.
@@ -0,0 +1,15 @@
1
+ import { X as XLockConfig } from './core-D4eszYbi.mjs';
2
+
3
+ /**
4
+ * x-lock middleware for Hono (Bun / Cloudflare Workers / Deno / Node)
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { xlockMiddleware } from "@xlock/node/hono";
9
+ * app.use("/api/auth/*", xlockMiddleware({ siteKey: "sk_..." }));
10
+ * ```
11
+ */
12
+
13
+ declare function xlockMiddleware(options?: XLockConfig): (c: any, next: () => Promise<void>) => Promise<any>;
14
+
15
+ export { XLockConfig, xlockMiddleware };
package/dist/hono.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { X as XLockConfig } from './core-D4eszYbi.js';
2
+
3
+ /**
4
+ * x-lock middleware for Hono (Bun / Cloudflare Workers / Deno / Node)
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { xlockMiddleware } from "@xlock/node/hono";
9
+ * app.use("/api/auth/*", xlockMiddleware({ siteKey: "sk_..." }));
10
+ * ```
11
+ */
12
+
13
+ declare function xlockMiddleware(options?: XLockConfig): (c: any, next: () => Promise<void>) => Promise<any>;
14
+
15
+ export { XLockConfig, xlockMiddleware };
package/dist/hono.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/hono.ts
21
+ var hono_exports = {};
22
+ __export(hono_exports, {
23
+ xlockMiddleware: () => xlockMiddleware
24
+ });
25
+ module.exports = __toCommonJS(hono_exports);
26
+
27
+ // src/core.ts
28
+ var DEFAULT_API_URL = "https://api.x-lock.dev";
29
+ async function enforce(apiUrl, siteKey, token, path) {
30
+ let enforceUrl;
31
+ let body;
32
+ if (token.startsWith("v3.")) {
33
+ const sessionId = token.split(".")[1];
34
+ enforceUrl = `${apiUrl}/v3/session/enforce`;
35
+ body = { sessionId, siteKey };
36
+ if (path) body.path = path;
37
+ } else {
38
+ enforceUrl = `${apiUrl}/v1/enforce`;
39
+ body = { token, siteKey };
40
+ if (path) body.path = path;
41
+ }
42
+ const response = await fetch(enforceUrl, {
43
+ method: "POST",
44
+ headers: { "Content-Type": "application/json" },
45
+ body: JSON.stringify(body)
46
+ });
47
+ if (response.status === 403) {
48
+ const data = await response.json();
49
+ return { blocked: true, reason: data.reason };
50
+ }
51
+ if (!response.ok) {
52
+ return { blocked: false, error: true };
53
+ }
54
+ return { blocked: false };
55
+ }
56
+
57
+ // src/hono.ts
58
+ function xlockMiddleware(options = {}) {
59
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
60
+ const siteKey = options.siteKey || (typeof process !== "undefined" ? process.env?.XLOCK_SITE_KEY : void 0);
61
+ const failOpen = options.failOpen !== false;
62
+ return async (c, next) => {
63
+ if (!siteKey || c.req.method !== "POST") {
64
+ return next();
65
+ }
66
+ const token = c.req.header("x-lock");
67
+ if (!token) {
68
+ return c.json({ error: "Blocked by x-lock: missing token" }, 403);
69
+ }
70
+ try {
71
+ const result = await enforce(apiUrl, siteKey, token, c.req.path);
72
+ if (result.blocked) {
73
+ return c.json({ error: "Blocked by x-lock", reason: result.reason }, 403);
74
+ }
75
+ if (result.error && !failOpen) {
76
+ return c.json({ error: "x-lock verification failed" }, 403);
77
+ }
78
+ } catch (err) {
79
+ console.error("[x-lock] Enforcement error:", err);
80
+ if (!failOpen) {
81
+ return c.json({ error: "x-lock verification failed" }, 403);
82
+ }
83
+ }
84
+ return next();
85
+ };
86
+ }
87
+ // Annotate the CommonJS export names for ESM import in node:
88
+ 0 && (module.exports = {
89
+ xlockMiddleware
90
+ });
91
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hono.ts","../src/core.ts"],"sourcesContent":["/**\n * x-lock middleware for Hono (Bun / Cloudflare Workers / Deno / Node)\n *\n * @example\n * ```ts\n * import { xlockMiddleware } from \"@xlock/node/hono\";\n * app.use(\"/api/auth/*\", xlockMiddleware({ siteKey: \"sk_...\" }));\n * ```\n */\n\nimport { enforce, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport type { XLockConfig };\n\nexport function xlockMiddleware(options: XLockConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey =\n options.siteKey ||\n (typeof process !== \"undefined\" ? process.env?.XLOCK_SITE_KEY : undefined);\n const failOpen = options.failOpen !== false;\n\n return async (c: any, next: () => Promise<void>) => {\n if (!siteKey || c.req.method !== \"POST\") {\n return next();\n }\n\n const token = c.req.header(\"x-lock\");\n\n if (!token) {\n return c.json({ error: \"Blocked by x-lock: missing token\" }, 403);\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, c.req.path);\n\n if (result.blocked) {\n return c.json({ error: \"Blocked by x-lock\", reason: result.reason }, 403);\n }\n\n if (result.error && !failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n } catch (err) {\n console.error(\"[x-lock] Enforcement error:\", err);\n if (!failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n }\n\n return next();\n };\n}\n","/**\n * Core x-lock enforcement logic shared across all framework adapters.\n */\n\nexport const DEFAULT_API_URL = \"https://api.x-lock.dev\";\n\nexport interface XLockConfig {\n /** Your x-lock site key (or set XLOCK_SITE_KEY env var) */\n siteKey?: string;\n /** x-lock API URL (default: https://api.x-lock.dev) */\n apiUrl?: string;\n /** Allow requests through on API errors (default: true) */\n failOpen?: boolean;\n}\n\nexport interface EnforceResult {\n blocked: boolean;\n reason?: string;\n error?: boolean;\n}\n\n/**\n * Call the x-lock enforcement API to verify a token.\n */\nexport async function enforce(\n apiUrl: string,\n siteKey: string,\n token: string,\n path?: string\n): Promise<EnforceResult> {\n let enforceUrl: string;\n let body: Record<string, string>;\n\n if (token.startsWith(\"v3.\")) {\n const sessionId = token.split(\".\")[1];\n enforceUrl = `${apiUrl}/v3/session/enforce`;\n body = { sessionId, siteKey };\n if (path) body.path = path;\n } else {\n enforceUrl = `${apiUrl}/v1/enforce`;\n body = { token, siteKey };\n if (path) body.path = path;\n }\n\n const response = await fetch(enforceUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (response.status === 403) {\n const data = (await response.json()) as { reason?: string };\n return { blocked: true, reason: data.reason };\n }\n\n if (!response.ok) {\n return { blocked: false, error: true };\n }\n\n return { blocked: false };\n}\n\n/**\n * Resolve site key from options or environment.\n */\nexport function resolveSiteKey(siteKey?: string): string | undefined {\n return siteKey || process.env.XLOCK_SITE_KEY;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIO,IAAM,kBAAkB;AAoB/B,eAAsB,QACpB,QACA,SACA,OACA,MACwB;AACxB,MAAI;AACJ,MAAI;AAEJ,MAAI,MAAM,WAAW,KAAK,GAAG;AAC3B,UAAM,YAAY,MAAM,MAAM,GAAG,EAAE,CAAC;AACpC,iBAAa,GAAG,MAAM;AACtB,WAAO,EAAE,WAAW,QAAQ;AAC5B,QAAI,KAAM,MAAK,OAAO;AAAA,EACxB,OAAO;AACL,iBAAa,GAAG,MAAM;AACtB,WAAO,EAAE,OAAO,QAAQ;AACxB,QAAI,KAAM,MAAK,OAAO;AAAA,EACxB;AAEA,QAAM,WAAW,MAAM,MAAM,YAAY;AAAA,IACvC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,SAAS,WAAW,KAAK;AAC3B,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,EAAE,SAAS,MAAM,QAAQ,KAAK,OAAO;AAAA,EAC9C;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,WAAO,EAAE,SAAS,OAAO,OAAO,KAAK;AAAA,EACvC;AAEA,SAAO,EAAE,SAAS,MAAM;AAC1B;;;AD9CO,SAAS,gBAAgB,UAAuB,CAAC,GAAG;AACzD,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,UACJ,QAAQ,YACP,OAAO,YAAY,cAAc,QAAQ,KAAK,iBAAiB;AAClE,QAAM,WAAW,QAAQ,aAAa;AAEtC,SAAO,OAAO,GAAQ,SAA8B;AAClD,QAAI,CAAC,WAAW,EAAE,IAAI,WAAW,QAAQ;AACvC,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAAQ,EAAE,IAAI,OAAO,QAAQ;AAEnC,QAAI,CAAC,OAAO;AACV,aAAO,EAAE,KAAK,EAAE,OAAO,mCAAmC,GAAG,GAAG;AAAA,IAClE;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,SAAS,OAAO,EAAE,IAAI,IAAI;AAE/D,UAAI,OAAO,SAAS;AAClB,eAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,QAAQ,OAAO,OAAO,GAAG,GAAG;AAAA,MAC1E;AAEA,UAAI,OAAO,SAAS,CAAC,UAAU;AAC7B,eAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;AAAA,MAC5D;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,+BAA+B,GAAG;AAChD,UAAI,CAAC,UAAU;AACb,eAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;AAAA,MAC5D;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AACF;","names":[]}
package/dist/hono.mjs ADDED
@@ -0,0 +1,64 @@
1
+ // src/core.ts
2
+ var DEFAULT_API_URL = "https://api.x-lock.dev";
3
+ async function enforce(apiUrl, siteKey, token, path) {
4
+ let enforceUrl;
5
+ let body;
6
+ if (token.startsWith("v3.")) {
7
+ const sessionId = token.split(".")[1];
8
+ enforceUrl = `${apiUrl}/v3/session/enforce`;
9
+ body = { sessionId, siteKey };
10
+ if (path) body.path = path;
11
+ } else {
12
+ enforceUrl = `${apiUrl}/v1/enforce`;
13
+ body = { token, siteKey };
14
+ if (path) body.path = path;
15
+ }
16
+ const response = await fetch(enforceUrl, {
17
+ method: "POST",
18
+ headers: { "Content-Type": "application/json" },
19
+ body: JSON.stringify(body)
20
+ });
21
+ if (response.status === 403) {
22
+ const data = await response.json();
23
+ return { blocked: true, reason: data.reason };
24
+ }
25
+ if (!response.ok) {
26
+ return { blocked: false, error: true };
27
+ }
28
+ return { blocked: false };
29
+ }
30
+
31
+ // src/hono.ts
32
+ function xlockMiddleware(options = {}) {
33
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
34
+ const siteKey = options.siteKey || (typeof process !== "undefined" ? process.env?.XLOCK_SITE_KEY : void 0);
35
+ const failOpen = options.failOpen !== false;
36
+ return async (c, next) => {
37
+ if (!siteKey || c.req.method !== "POST") {
38
+ return next();
39
+ }
40
+ const token = c.req.header("x-lock");
41
+ if (!token) {
42
+ return c.json({ error: "Blocked by x-lock: missing token" }, 403);
43
+ }
44
+ try {
45
+ const result = await enforce(apiUrl, siteKey, token, c.req.path);
46
+ if (result.blocked) {
47
+ return c.json({ error: "Blocked by x-lock", reason: result.reason }, 403);
48
+ }
49
+ if (result.error && !failOpen) {
50
+ return c.json({ error: "x-lock verification failed" }, 403);
51
+ }
52
+ } catch (err) {
53
+ console.error("[x-lock] Enforcement error:", err);
54
+ if (!failOpen) {
55
+ return c.json({ error: "x-lock verification failed" }, 403);
56
+ }
57
+ }
58
+ return next();
59
+ };
60
+ }
61
+ export {
62
+ xlockMiddleware
63
+ };
64
+ //# sourceMappingURL=hono.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core.ts","../src/hono.ts"],"sourcesContent":["/**\n * Core x-lock enforcement logic shared across all framework adapters.\n */\n\nexport const DEFAULT_API_URL = \"https://api.x-lock.dev\";\n\nexport interface XLockConfig {\n /** Your x-lock site key (or set XLOCK_SITE_KEY env var) */\n siteKey?: string;\n /** x-lock API URL (default: https://api.x-lock.dev) */\n apiUrl?: string;\n /** Allow requests through on API errors (default: true) */\n failOpen?: boolean;\n}\n\nexport interface EnforceResult {\n blocked: boolean;\n reason?: string;\n error?: boolean;\n}\n\n/**\n * Call the x-lock enforcement API to verify a token.\n */\nexport async function enforce(\n apiUrl: string,\n siteKey: string,\n token: string,\n path?: string\n): Promise<EnforceResult> {\n let enforceUrl: string;\n let body: Record<string, string>;\n\n if (token.startsWith(\"v3.\")) {\n const sessionId = token.split(\".\")[1];\n enforceUrl = `${apiUrl}/v3/session/enforce`;\n body = { sessionId, siteKey };\n if (path) body.path = path;\n } else {\n enforceUrl = `${apiUrl}/v1/enforce`;\n body = { token, siteKey };\n if (path) body.path = path;\n }\n\n const response = await fetch(enforceUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (response.status === 403) {\n const data = (await response.json()) as { reason?: string };\n return { blocked: true, reason: data.reason };\n }\n\n if (!response.ok) {\n return { blocked: false, error: true };\n }\n\n return { blocked: false };\n}\n\n/**\n * Resolve site key from options or environment.\n */\nexport function resolveSiteKey(siteKey?: string): string | undefined {\n return siteKey || process.env.XLOCK_SITE_KEY;\n}\n","/**\n * x-lock middleware for Hono (Bun / Cloudflare Workers / Deno / Node)\n *\n * @example\n * ```ts\n * import { xlockMiddleware } from \"@xlock/node/hono\";\n * app.use(\"/api/auth/*\", xlockMiddleware({ siteKey: \"sk_...\" }));\n * ```\n */\n\nimport { enforce, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport type { XLockConfig };\n\nexport function xlockMiddleware(options: XLockConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey =\n options.siteKey ||\n (typeof process !== \"undefined\" ? process.env?.XLOCK_SITE_KEY : undefined);\n const failOpen = options.failOpen !== false;\n\n return async (c: any, next: () => Promise<void>) => {\n if (!siteKey || c.req.method !== \"POST\") {\n return next();\n }\n\n const token = c.req.header(\"x-lock\");\n\n if (!token) {\n return c.json({ error: \"Blocked by x-lock: missing token\" }, 403);\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, c.req.path);\n\n if (result.blocked) {\n return c.json({ error: \"Blocked by x-lock\", reason: result.reason }, 403);\n }\n\n if (result.error && !failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n } catch (err) {\n console.error(\"[x-lock] Enforcement error:\", err);\n if (!failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n }\n\n return next();\n };\n}\n"],"mappings":";AAIO,IAAM,kBAAkB;AAoB/B,eAAsB,QACpB,QACA,SACA,OACA,MACwB;AACxB,MAAI;AACJ,MAAI;AAEJ,MAAI,MAAM,WAAW,KAAK,GAAG;AAC3B,UAAM,YAAY,MAAM,MAAM,GAAG,EAAE,CAAC;AACpC,iBAAa,GAAG,MAAM;AACtB,WAAO,EAAE,WAAW,QAAQ;AAC5B,QAAI,KAAM,MAAK,OAAO;AAAA,EACxB,OAAO;AACL,iBAAa,GAAG,MAAM;AACtB,WAAO,EAAE,OAAO,QAAQ;AACxB,QAAI,KAAM,MAAK,OAAO;AAAA,EACxB;AAEA,QAAM,WAAW,MAAM,MAAM,YAAY;AAAA,IACvC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,SAAS,WAAW,KAAK;AAC3B,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,EAAE,SAAS,MAAM,QAAQ,KAAK,OAAO;AAAA,EAC9C;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,WAAO,EAAE,SAAS,OAAO,OAAO,KAAK;AAAA,EACvC;AAEA,SAAO,EAAE,SAAS,MAAM;AAC1B;;;AC9CO,SAAS,gBAAgB,UAAuB,CAAC,GAAG;AACzD,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,UACJ,QAAQ,YACP,OAAO,YAAY,cAAc,QAAQ,KAAK,iBAAiB;AAClE,QAAM,WAAW,QAAQ,aAAa;AAEtC,SAAO,OAAO,GAAQ,SAA8B;AAClD,QAAI,CAAC,WAAW,EAAE,IAAI,WAAW,QAAQ;AACvC,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAAQ,EAAE,IAAI,OAAO,QAAQ;AAEnC,QAAI,CAAC,OAAO;AACV,aAAO,EAAE,KAAK,EAAE,OAAO,mCAAmC,GAAG,GAAG;AAAA,IAClE;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,SAAS,OAAO,EAAE,IAAI,IAAI;AAE/D,UAAI,OAAO,SAAS;AAClB,eAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,QAAQ,OAAO,OAAO,GAAG,GAAG;AAAA,MAC1E;AAEA,UAAI,OAAO,SAAS,CAAC,UAAU;AAC7B,eAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;AAAA,MAC5D;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,+BAA+B,GAAG;AAChD,UAAI,CAAC,UAAU;AACb,eAAO,EAAE,KAAK,EAAE,OAAO,6BAA6B,GAAG,GAAG;AAAA,MAC5D;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AACF;","names":[]}
@@ -0,0 +1,4 @@
1
+ export { D as DEFAULT_API_URL, E as EnforceResult, X as XLockConfig, e as verify } from './core-D4eszYbi.mjs';
2
+ export { xlockMiddleware as expressMiddleware } from './express.mjs';
3
+ export { xlockMiddleware as koaMiddleware } from './koa.mjs';
4
+ export { xlockMiddleware as honoMiddleware } from './hono.mjs';
@@ -0,0 +1,4 @@
1
+ export { D as DEFAULT_API_URL, E as EnforceResult, X as XLockConfig, e as verify } from './core-D4eszYbi.js';
2
+ export { xlockMiddleware as expressMiddleware } from './express.js';
3
+ export { xlockMiddleware as koaMiddleware } from './koa.js';
4
+ export { xlockMiddleware as honoMiddleware } from './hono.js';
package/dist/index.js ADDED
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULT_API_URL: () => DEFAULT_API_URL,
24
+ expressMiddleware: () => xlockMiddleware,
25
+ honoMiddleware: () => xlockMiddleware3,
26
+ koaMiddleware: () => xlockMiddleware2,
27
+ verify: () => enforce
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/core.ts
32
+ var DEFAULT_API_URL = "https://api.x-lock.dev";
33
+ async function enforce(apiUrl, siteKey, token, path) {
34
+ let enforceUrl;
35
+ let body;
36
+ if (token.startsWith("v3.")) {
37
+ const sessionId = token.split(".")[1];
38
+ enforceUrl = `${apiUrl}/v3/session/enforce`;
39
+ body = { sessionId, siteKey };
40
+ if (path) body.path = path;
41
+ } else {
42
+ enforceUrl = `${apiUrl}/v1/enforce`;
43
+ body = { token, siteKey };
44
+ if (path) body.path = path;
45
+ }
46
+ const response = await fetch(enforceUrl, {
47
+ method: "POST",
48
+ headers: { "Content-Type": "application/json" },
49
+ body: JSON.stringify(body)
50
+ });
51
+ if (response.status === 403) {
52
+ const data = await response.json();
53
+ return { blocked: true, reason: data.reason };
54
+ }
55
+ if (!response.ok) {
56
+ return { blocked: false, error: true };
57
+ }
58
+ return { blocked: false };
59
+ }
60
+ function resolveSiteKey(siteKey) {
61
+ return siteKey || process.env.XLOCK_SITE_KEY;
62
+ }
63
+
64
+ // src/express.ts
65
+ function xlockMiddleware(options = {}) {
66
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
67
+ const siteKey = resolveSiteKey(options.siteKey);
68
+ const failOpen = options.failOpen !== false;
69
+ if (!siteKey) {
70
+ console.warn("[x-lock] No site key configured \u2014 skipping enforcement");
71
+ return (_req, _res, next) => next();
72
+ }
73
+ return async (req, res, next) => {
74
+ const token = req.headers["x-lock"];
75
+ if (!token) {
76
+ return res.status(403).json({ error: "Blocked by x-lock: missing token" });
77
+ }
78
+ try {
79
+ const result = await enforce(apiUrl, siteKey, token, req.originalUrl || req.url);
80
+ if (result.blocked) {
81
+ return res.status(403).json({ error: "Blocked by x-lock", reason: result.reason });
82
+ }
83
+ if (result.error && !failOpen) {
84
+ return res.status(403).json({ error: "x-lock verification failed" });
85
+ }
86
+ } catch (err) {
87
+ console.error("[x-lock] Enforcement error:", err.message);
88
+ if (!failOpen) {
89
+ return res.status(403).json({ error: "x-lock verification failed" });
90
+ }
91
+ }
92
+ next();
93
+ };
94
+ }
95
+
96
+ // src/koa.ts
97
+ function xlockMiddleware2(options = {}) {
98
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
99
+ const siteKey = resolveSiteKey(options.siteKey);
100
+ const failOpen = options.failOpen !== false;
101
+ const routes = options.routes || [];
102
+ if (!siteKey) {
103
+ console.warn("[x-lock] No site key configured \u2014 skipping enforcement");
104
+ return async (_ctx, next) => next();
105
+ }
106
+ return async (ctx, next) => {
107
+ if (ctx.method !== "POST") return next();
108
+ if (routes.length > 0 && !routes.some((r) => ctx.path.startsWith(r))) {
109
+ return next();
110
+ }
111
+ const token = ctx.get("x-lock");
112
+ if (!token) {
113
+ ctx.status = 403;
114
+ ctx.body = { error: "Blocked by x-lock: missing token" };
115
+ return;
116
+ }
117
+ try {
118
+ const result = await enforce(apiUrl, siteKey, token, ctx.path);
119
+ if (result.blocked) {
120
+ ctx.status = 403;
121
+ ctx.body = { error: "Blocked by x-lock", reason: result.reason };
122
+ return;
123
+ }
124
+ if (result.error && !failOpen) {
125
+ ctx.status = 403;
126
+ ctx.body = { error: "x-lock verification failed" };
127
+ return;
128
+ }
129
+ } catch (err) {
130
+ console.error("[x-lock] Enforcement error:", err.message);
131
+ if (!failOpen) {
132
+ ctx.status = 403;
133
+ ctx.body = { error: "x-lock verification failed" };
134
+ return;
135
+ }
136
+ }
137
+ return next();
138
+ };
139
+ }
140
+
141
+ // src/hono.ts
142
+ function xlockMiddleware3(options = {}) {
143
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
144
+ const siteKey = options.siteKey || (typeof process !== "undefined" ? process.env?.XLOCK_SITE_KEY : void 0);
145
+ const failOpen = options.failOpen !== false;
146
+ return async (c, next) => {
147
+ if (!siteKey || c.req.method !== "POST") {
148
+ return next();
149
+ }
150
+ const token = c.req.header("x-lock");
151
+ if (!token) {
152
+ return c.json({ error: "Blocked by x-lock: missing token" }, 403);
153
+ }
154
+ try {
155
+ const result = await enforce(apiUrl, siteKey, token, c.req.path);
156
+ if (result.blocked) {
157
+ return c.json({ error: "Blocked by x-lock", reason: result.reason }, 403);
158
+ }
159
+ if (result.error && !failOpen) {
160
+ return c.json({ error: "x-lock verification failed" }, 403);
161
+ }
162
+ } catch (err) {
163
+ console.error("[x-lock] Enforcement error:", err);
164
+ if (!failOpen) {
165
+ return c.json({ error: "x-lock verification failed" }, 403);
166
+ }
167
+ }
168
+ return next();
169
+ };
170
+ }
171
+ // Annotate the CommonJS export names for ESM import in node:
172
+ 0 && (module.exports = {
173
+ DEFAULT_API_URL,
174
+ expressMiddleware,
175
+ honoMiddleware,
176
+ koaMiddleware,
177
+ verify
178
+ });
179
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core.ts","../src/express.ts","../src/koa.ts","../src/hono.ts"],"sourcesContent":["/**\n * @xlock/node — invisible bot protection middleware for Node.js\n *\n * Framework-specific imports:\n * import { xlockMiddleware } from \"@xlock/node/express\";\n * import { xlockPlugin } from \"@xlock/node/fastify\";\n * import { xlockMiddleware } from \"@xlock/node/koa\";\n * import { withXlock } from \"@xlock/node/next\";\n * import { xlockMiddleware } from \"@xlock/node/hono\";\n *\n * Or use the core verify function directly:\n * import { verify } from \"@xlock/node\";\n */\n\nexport { enforce as verify, DEFAULT_API_URL, type XLockConfig, type EnforceResult } from \"./core\";\n\n// Re-export framework adapters that don't require peer deps at import time\nexport { xlockMiddleware as expressMiddleware } from \"./express\";\nexport { xlockMiddleware as koaMiddleware } from \"./koa\";\nexport { xlockMiddleware as honoMiddleware } from \"./hono\";\n// Fastify and Next.js should be imported via subpath:\n// import { xlockPlugin } from \"@xlock/node/fastify\";\n// import { withXlock } from \"@xlock/node/next\";\n","/**\n * Core x-lock enforcement logic shared across all framework adapters.\n */\n\nexport const DEFAULT_API_URL = \"https://api.x-lock.dev\";\n\nexport interface XLockConfig {\n /** Your x-lock site key (or set XLOCK_SITE_KEY env var) */\n siteKey?: string;\n /** x-lock API URL (default: https://api.x-lock.dev) */\n apiUrl?: string;\n /** Allow requests through on API errors (default: true) */\n failOpen?: boolean;\n}\n\nexport interface EnforceResult {\n blocked: boolean;\n reason?: string;\n error?: boolean;\n}\n\n/**\n * Call the x-lock enforcement API to verify a token.\n */\nexport async function enforce(\n apiUrl: string,\n siteKey: string,\n token: string,\n path?: string\n): Promise<EnforceResult> {\n let enforceUrl: string;\n let body: Record<string, string>;\n\n if (token.startsWith(\"v3.\")) {\n const sessionId = token.split(\".\")[1];\n enforceUrl = `${apiUrl}/v3/session/enforce`;\n body = { sessionId, siteKey };\n if (path) body.path = path;\n } else {\n enforceUrl = `${apiUrl}/v1/enforce`;\n body = { token, siteKey };\n if (path) body.path = path;\n }\n\n const response = await fetch(enforceUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (response.status === 403) {\n const data = (await response.json()) as { reason?: string };\n return { blocked: true, reason: data.reason };\n }\n\n if (!response.ok) {\n return { blocked: false, error: true };\n }\n\n return { blocked: false };\n}\n\n/**\n * Resolve site key from options or environment.\n */\nexport function resolveSiteKey(siteKey?: string): string | undefined {\n return siteKey || process.env.XLOCK_SITE_KEY;\n}\n","/**\n * x-lock middleware for Express.js\n *\n * @example\n * ```js\n * import { xlockMiddleware } from \"@xlock/node/express\";\n * app.use(\"/api/auth\", xlockMiddleware({ siteKey: \"sk_...\" }));\n * ```\n */\n\nimport { enforce, resolveSiteKey, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport type { XLockConfig };\n\nexport function xlockMiddleware(options: XLockConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey = resolveSiteKey(options.siteKey);\n const failOpen = options.failOpen !== false;\n\n if (!siteKey) {\n console.warn(\"[x-lock] No site key configured — skipping enforcement\");\n return (_req: any, _res: any, next: () => void) => next();\n }\n\n return async (req: any, res: any, next: () => void) => {\n const token = req.headers[\"x-lock\"];\n\n if (!token) {\n return res.status(403).json({ error: \"Blocked by x-lock: missing token\" });\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, req.originalUrl || req.url);\n\n if (result.blocked) {\n return res.status(403).json({ error: \"Blocked by x-lock\", reason: result.reason });\n }\n\n if (result.error && !failOpen) {\n return res.status(403).json({ error: \"x-lock verification failed\" });\n }\n } catch (err: any) {\n console.error(\"[x-lock] Enforcement error:\", err.message);\n if (!failOpen) {\n return res.status(403).json({ error: \"x-lock verification failed\" });\n }\n }\n\n next();\n };\n}\n","/**\n * x-lock middleware for Koa\n *\n * @example\n * ```js\n * import { xlockMiddleware } from \"@xlock/node/koa\";\n * router.post(\"/api/auth/login\", xlockMiddleware({ siteKey: \"sk_...\" }), loginHandler);\n * ```\n */\n\nimport { enforce, resolveSiteKey, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport interface XLockKoaConfig extends XLockConfig {\n /** Route prefixes to protect */\n routes?: string[];\n}\n\nexport function xlockMiddleware(options: XLockKoaConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey = resolveSiteKey(options.siteKey);\n const failOpen = options.failOpen !== false;\n const routes = options.routes || [];\n\n if (!siteKey) {\n console.warn(\"[x-lock] No site key configured — skipping enforcement\");\n return async (_ctx: any, next: () => Promise<void>) => next();\n }\n\n return async (ctx: any, next: () => Promise<void>) => {\n if (ctx.method !== \"POST\") return next();\n\n if (routes.length > 0 && !routes.some((r: string) => ctx.path.startsWith(r))) {\n return next();\n }\n\n const token = ctx.get(\"x-lock\");\n\n if (!token) {\n ctx.status = 403;\n ctx.body = { error: \"Blocked by x-lock: missing token\" };\n return;\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, ctx.path);\n\n if (result.blocked) {\n ctx.status = 403;\n ctx.body = { error: \"Blocked by x-lock\", reason: result.reason };\n return;\n }\n\n if (result.error && !failOpen) {\n ctx.status = 403;\n ctx.body = { error: \"x-lock verification failed\" };\n return;\n }\n } catch (err: any) {\n console.error(\"[x-lock] Enforcement error:\", err.message);\n if (!failOpen) {\n ctx.status = 403;\n ctx.body = { error: \"x-lock verification failed\" };\n return;\n }\n }\n\n return next();\n };\n}\n","/**\n * x-lock middleware for Hono (Bun / Cloudflare Workers / Deno / Node)\n *\n * @example\n * ```ts\n * import { xlockMiddleware } from \"@xlock/node/hono\";\n * app.use(\"/api/auth/*\", xlockMiddleware({ siteKey: \"sk_...\" }));\n * ```\n */\n\nimport { enforce, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport type { XLockConfig };\n\nexport function xlockMiddleware(options: XLockConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey =\n options.siteKey ||\n (typeof process !== \"undefined\" ? process.env?.XLOCK_SITE_KEY : undefined);\n const failOpen = options.failOpen !== false;\n\n return async (c: any, next: () => Promise<void>) => {\n if (!siteKey || c.req.method !== \"POST\") {\n return next();\n }\n\n const token = c.req.header(\"x-lock\");\n\n if (!token) {\n return c.json({ error: \"Blocked by x-lock: missing token\" }, 403);\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, c.req.path);\n\n if (result.blocked) {\n return c.json({ error: \"Blocked by x-lock\", reason: result.reason }, 403);\n }\n\n if (result.error && !failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n } catch (err) {\n console.error(\"[x-lock] Enforcement error:\", err);\n if (!failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n }\n\n return next();\n };\n}\n"],"mappings":"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","names":["xlockMiddleware","xlockMiddleware","xlockMiddleware"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,148 @@
1
+ // src/core.ts
2
+ var DEFAULT_API_URL = "https://api.x-lock.dev";
3
+ async function enforce(apiUrl, siteKey, token, path) {
4
+ let enforceUrl;
5
+ let body;
6
+ if (token.startsWith("v3.")) {
7
+ const sessionId = token.split(".")[1];
8
+ enforceUrl = `${apiUrl}/v3/session/enforce`;
9
+ body = { sessionId, siteKey };
10
+ if (path) body.path = path;
11
+ } else {
12
+ enforceUrl = `${apiUrl}/v1/enforce`;
13
+ body = { token, siteKey };
14
+ if (path) body.path = path;
15
+ }
16
+ const response = await fetch(enforceUrl, {
17
+ method: "POST",
18
+ headers: { "Content-Type": "application/json" },
19
+ body: JSON.stringify(body)
20
+ });
21
+ if (response.status === 403) {
22
+ const data = await response.json();
23
+ return { blocked: true, reason: data.reason };
24
+ }
25
+ if (!response.ok) {
26
+ return { blocked: false, error: true };
27
+ }
28
+ return { blocked: false };
29
+ }
30
+ function resolveSiteKey(siteKey) {
31
+ return siteKey || process.env.XLOCK_SITE_KEY;
32
+ }
33
+
34
+ // src/express.ts
35
+ function xlockMiddleware(options = {}) {
36
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
37
+ const siteKey = resolveSiteKey(options.siteKey);
38
+ const failOpen = options.failOpen !== false;
39
+ if (!siteKey) {
40
+ console.warn("[x-lock] No site key configured \u2014 skipping enforcement");
41
+ return (_req, _res, next) => next();
42
+ }
43
+ return async (req, res, next) => {
44
+ const token = req.headers["x-lock"];
45
+ if (!token) {
46
+ return res.status(403).json({ error: "Blocked by x-lock: missing token" });
47
+ }
48
+ try {
49
+ const result = await enforce(apiUrl, siteKey, token, req.originalUrl || req.url);
50
+ if (result.blocked) {
51
+ return res.status(403).json({ error: "Blocked by x-lock", reason: result.reason });
52
+ }
53
+ if (result.error && !failOpen) {
54
+ return res.status(403).json({ error: "x-lock verification failed" });
55
+ }
56
+ } catch (err) {
57
+ console.error("[x-lock] Enforcement error:", err.message);
58
+ if (!failOpen) {
59
+ return res.status(403).json({ error: "x-lock verification failed" });
60
+ }
61
+ }
62
+ next();
63
+ };
64
+ }
65
+
66
+ // src/koa.ts
67
+ function xlockMiddleware2(options = {}) {
68
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
69
+ const siteKey = resolveSiteKey(options.siteKey);
70
+ const failOpen = options.failOpen !== false;
71
+ const routes = options.routes || [];
72
+ if (!siteKey) {
73
+ console.warn("[x-lock] No site key configured \u2014 skipping enforcement");
74
+ return async (_ctx, next) => next();
75
+ }
76
+ return async (ctx, next) => {
77
+ if (ctx.method !== "POST") return next();
78
+ if (routes.length > 0 && !routes.some((r) => ctx.path.startsWith(r))) {
79
+ return next();
80
+ }
81
+ const token = ctx.get("x-lock");
82
+ if (!token) {
83
+ ctx.status = 403;
84
+ ctx.body = { error: "Blocked by x-lock: missing token" };
85
+ return;
86
+ }
87
+ try {
88
+ const result = await enforce(apiUrl, siteKey, token, ctx.path);
89
+ if (result.blocked) {
90
+ ctx.status = 403;
91
+ ctx.body = { error: "Blocked by x-lock", reason: result.reason };
92
+ return;
93
+ }
94
+ if (result.error && !failOpen) {
95
+ ctx.status = 403;
96
+ ctx.body = { error: "x-lock verification failed" };
97
+ return;
98
+ }
99
+ } catch (err) {
100
+ console.error("[x-lock] Enforcement error:", err.message);
101
+ if (!failOpen) {
102
+ ctx.status = 403;
103
+ ctx.body = { error: "x-lock verification failed" };
104
+ return;
105
+ }
106
+ }
107
+ return next();
108
+ };
109
+ }
110
+
111
+ // src/hono.ts
112
+ function xlockMiddleware3(options = {}) {
113
+ const apiUrl = options.apiUrl || DEFAULT_API_URL;
114
+ const siteKey = options.siteKey || (typeof process !== "undefined" ? process.env?.XLOCK_SITE_KEY : void 0);
115
+ const failOpen = options.failOpen !== false;
116
+ return async (c, next) => {
117
+ if (!siteKey || c.req.method !== "POST") {
118
+ return next();
119
+ }
120
+ const token = c.req.header("x-lock");
121
+ if (!token) {
122
+ return c.json({ error: "Blocked by x-lock: missing token" }, 403);
123
+ }
124
+ try {
125
+ const result = await enforce(apiUrl, siteKey, token, c.req.path);
126
+ if (result.blocked) {
127
+ return c.json({ error: "Blocked by x-lock", reason: result.reason }, 403);
128
+ }
129
+ if (result.error && !failOpen) {
130
+ return c.json({ error: "x-lock verification failed" }, 403);
131
+ }
132
+ } catch (err) {
133
+ console.error("[x-lock] Enforcement error:", err);
134
+ if (!failOpen) {
135
+ return c.json({ error: "x-lock verification failed" }, 403);
136
+ }
137
+ }
138
+ return next();
139
+ };
140
+ }
141
+ export {
142
+ DEFAULT_API_URL,
143
+ xlockMiddleware as expressMiddleware,
144
+ xlockMiddleware3 as honoMiddleware,
145
+ xlockMiddleware2 as koaMiddleware,
146
+ enforce as verify
147
+ };
148
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core.ts","../src/express.ts","../src/koa.ts","../src/hono.ts"],"sourcesContent":["/**\n * Core x-lock enforcement logic shared across all framework adapters.\n */\n\nexport const DEFAULT_API_URL = \"https://api.x-lock.dev\";\n\nexport interface XLockConfig {\n /** Your x-lock site key (or set XLOCK_SITE_KEY env var) */\n siteKey?: string;\n /** x-lock API URL (default: https://api.x-lock.dev) */\n apiUrl?: string;\n /** Allow requests through on API errors (default: true) */\n failOpen?: boolean;\n}\n\nexport interface EnforceResult {\n blocked: boolean;\n reason?: string;\n error?: boolean;\n}\n\n/**\n * Call the x-lock enforcement API to verify a token.\n */\nexport async function enforce(\n apiUrl: string,\n siteKey: string,\n token: string,\n path?: string\n): Promise<EnforceResult> {\n let enforceUrl: string;\n let body: Record<string, string>;\n\n if (token.startsWith(\"v3.\")) {\n const sessionId = token.split(\".\")[1];\n enforceUrl = `${apiUrl}/v3/session/enforce`;\n body = { sessionId, siteKey };\n if (path) body.path = path;\n } else {\n enforceUrl = `${apiUrl}/v1/enforce`;\n body = { token, siteKey };\n if (path) body.path = path;\n }\n\n const response = await fetch(enforceUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (response.status === 403) {\n const data = (await response.json()) as { reason?: string };\n return { blocked: true, reason: data.reason };\n }\n\n if (!response.ok) {\n return { blocked: false, error: true };\n }\n\n return { blocked: false };\n}\n\n/**\n * Resolve site key from options or environment.\n */\nexport function resolveSiteKey(siteKey?: string): string | undefined {\n return siteKey || process.env.XLOCK_SITE_KEY;\n}\n","/**\n * x-lock middleware for Express.js\n *\n * @example\n * ```js\n * import { xlockMiddleware } from \"@xlock/node/express\";\n * app.use(\"/api/auth\", xlockMiddleware({ siteKey: \"sk_...\" }));\n * ```\n */\n\nimport { enforce, resolveSiteKey, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport type { XLockConfig };\n\nexport function xlockMiddleware(options: XLockConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey = resolveSiteKey(options.siteKey);\n const failOpen = options.failOpen !== false;\n\n if (!siteKey) {\n console.warn(\"[x-lock] No site key configured — skipping enforcement\");\n return (_req: any, _res: any, next: () => void) => next();\n }\n\n return async (req: any, res: any, next: () => void) => {\n const token = req.headers[\"x-lock\"];\n\n if (!token) {\n return res.status(403).json({ error: \"Blocked by x-lock: missing token\" });\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, req.originalUrl || req.url);\n\n if (result.blocked) {\n return res.status(403).json({ error: \"Blocked by x-lock\", reason: result.reason });\n }\n\n if (result.error && !failOpen) {\n return res.status(403).json({ error: \"x-lock verification failed\" });\n }\n } catch (err: any) {\n console.error(\"[x-lock] Enforcement error:\", err.message);\n if (!failOpen) {\n return res.status(403).json({ error: \"x-lock verification failed\" });\n }\n }\n\n next();\n };\n}\n","/**\n * x-lock middleware for Koa\n *\n * @example\n * ```js\n * import { xlockMiddleware } from \"@xlock/node/koa\";\n * router.post(\"/api/auth/login\", xlockMiddleware({ siteKey: \"sk_...\" }), loginHandler);\n * ```\n */\n\nimport { enforce, resolveSiteKey, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport interface XLockKoaConfig extends XLockConfig {\n /** Route prefixes to protect */\n routes?: string[];\n}\n\nexport function xlockMiddleware(options: XLockKoaConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey = resolveSiteKey(options.siteKey);\n const failOpen = options.failOpen !== false;\n const routes = options.routes || [];\n\n if (!siteKey) {\n console.warn(\"[x-lock] No site key configured — skipping enforcement\");\n return async (_ctx: any, next: () => Promise<void>) => next();\n }\n\n return async (ctx: any, next: () => Promise<void>) => {\n if (ctx.method !== \"POST\") return next();\n\n if (routes.length > 0 && !routes.some((r: string) => ctx.path.startsWith(r))) {\n return next();\n }\n\n const token = ctx.get(\"x-lock\");\n\n if (!token) {\n ctx.status = 403;\n ctx.body = { error: \"Blocked by x-lock: missing token\" };\n return;\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, ctx.path);\n\n if (result.blocked) {\n ctx.status = 403;\n ctx.body = { error: \"Blocked by x-lock\", reason: result.reason };\n return;\n }\n\n if (result.error && !failOpen) {\n ctx.status = 403;\n ctx.body = { error: \"x-lock verification failed\" };\n return;\n }\n } catch (err: any) {\n console.error(\"[x-lock] Enforcement error:\", err.message);\n if (!failOpen) {\n ctx.status = 403;\n ctx.body = { error: \"x-lock verification failed\" };\n return;\n }\n }\n\n return next();\n };\n}\n","/**\n * x-lock middleware for Hono (Bun / Cloudflare Workers / Deno / Node)\n *\n * @example\n * ```ts\n * import { xlockMiddleware } from \"@xlock/node/hono\";\n * app.use(\"/api/auth/*\", xlockMiddleware({ siteKey: \"sk_...\" }));\n * ```\n */\n\nimport { enforce, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport type { XLockConfig };\n\nexport function xlockMiddleware(options: XLockConfig = {}) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const siteKey =\n options.siteKey ||\n (typeof process !== \"undefined\" ? process.env?.XLOCK_SITE_KEY : undefined);\n const failOpen = options.failOpen !== false;\n\n return async (c: any, next: () => Promise<void>) => {\n if (!siteKey || c.req.method !== \"POST\") {\n return next();\n }\n\n const token = c.req.header(\"x-lock\");\n\n if (!token) {\n return c.json({ error: \"Blocked by x-lock: missing token\" }, 403);\n }\n\n try {\n const result = await enforce(apiUrl, siteKey, token, c.req.path);\n\n if (result.blocked) {\n return c.json({ error: \"Blocked by x-lock\", reason: result.reason }, 403);\n }\n\n if (result.error && !failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n } catch (err) {\n console.error(\"[x-lock] Enforcement error:\", err);\n if (!failOpen) {\n return c.json({ error: \"x-lock verification failed\" }, 403);\n }\n }\n\n return next();\n };\n}\n"],"mappings":"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","names":["xlockMiddleware","xlockMiddleware"]}
package/dist/koa.d.mts ADDED
@@ -0,0 +1,19 @@
1
+ import { X as XLockConfig } from './core-D4eszYbi.mjs';
2
+
3
+ /**
4
+ * x-lock middleware for Koa
5
+ *
6
+ * @example
7
+ * ```js
8
+ * import { xlockMiddleware } from "@xlock/node/koa";
9
+ * router.post("/api/auth/login", xlockMiddleware({ siteKey: "sk_..." }), loginHandler);
10
+ * ```
11
+ */
12
+
13
+ interface XLockKoaConfig extends XLockConfig {
14
+ /** Route prefixes to protect */
15
+ routes?: string[];
16
+ }
17
+ declare function xlockMiddleware(options?: XLockKoaConfig): (_ctx: any, next: () => Promise<void>) => Promise<void>;
18
+
19
+ export { type XLockKoaConfig, xlockMiddleware };
package/dist/koa.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { X as XLockConfig } from './core-D4eszYbi.js';
2
+
3
+ /**
4
+ * x-lock middleware for Koa
5
+ *
6
+ * @example
7
+ * ```js
8
+ * import { xlockMiddleware } from "@xlock/node/koa";
9
+ * router.post("/api/auth/login", xlockMiddleware({ siteKey: "sk_..." }), loginHandler);
10
+ * ```
11
+ */
12
+
13
+ interface XLockKoaConfig extends XLockConfig {
14
+ /** Route prefixes to protect */
15
+ routes?: string[];
16
+ }
17
+ declare function xlockMiddleware(options?: XLockKoaConfig): (_ctx: any, next: () => Promise<void>) => Promise<void>;
18
+
19
+ export { type XLockKoaConfig, xlockMiddleware };