@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.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/core-D4eszYbi.d.mts +23 -0
- package/dist/core-D4eszYbi.d.ts +23 -0
- package/dist/express.d.mts +15 -0
- package/dist/express.d.ts +15 -0
- package/dist/express.js +95 -0
- package/dist/express.js.map +1 -0
- package/dist/express.mjs +68 -0
- package/dist/express.mjs.map +1 -0
- package/dist/fastify.d.mts +19 -0
- package/dist/fastify.d.ts +19 -0
- package/dist/fastify.js +106 -0
- package/dist/fastify.js.map +1 -0
- package/dist/fastify.mjs +79 -0
- package/dist/fastify.mjs.map +1 -0
- package/dist/hono.d.mts +15 -0
- package/dist/hono.d.ts +15 -0
- package/dist/hono.js +91 -0
- package/dist/hono.js.map +1 -0
- package/dist/hono.mjs +64 -0
- package/dist/hono.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +148 -0
- package/dist/index.mjs.map +1 -0
- package/dist/koa.d.mts +19 -0
- package/dist/koa.d.ts +19 -0
- package/dist/koa.js +108 -0
- package/dist/koa.js.map +1 -0
- package/dist/koa.mjs +81 -0
- package/dist/koa.mjs.map +1 -0
- package/dist/next.d.mts +24 -0
- package/dist/next.d.ts +24 -0
- package/dist/next.js +101 -0
- package/dist/next.js.map +1 -0
- package/dist/next.mjs +74 -0
- package/dist/next.mjs.map +1 -0
- package/package.json +94 -0
package/dist/koa.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
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/koa.ts
|
|
21
|
+
var koa_exports = {};
|
|
22
|
+
__export(koa_exports, {
|
|
23
|
+
xlockMiddleware: () => xlockMiddleware
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(koa_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
|
+
function resolveSiteKey(siteKey) {
|
|
57
|
+
return siteKey || process.env.XLOCK_SITE_KEY;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/koa.ts
|
|
61
|
+
function xlockMiddleware(options = {}) {
|
|
62
|
+
const apiUrl = options.apiUrl || DEFAULT_API_URL;
|
|
63
|
+
const siteKey = resolveSiteKey(options.siteKey);
|
|
64
|
+
const failOpen = options.failOpen !== false;
|
|
65
|
+
const routes = options.routes || [];
|
|
66
|
+
if (!siteKey) {
|
|
67
|
+
console.warn("[x-lock] No site key configured \u2014 skipping enforcement");
|
|
68
|
+
return async (_ctx, next) => next();
|
|
69
|
+
}
|
|
70
|
+
return async (ctx, next) => {
|
|
71
|
+
if (ctx.method !== "POST") return next();
|
|
72
|
+
if (routes.length > 0 && !routes.some((r) => ctx.path.startsWith(r))) {
|
|
73
|
+
return next();
|
|
74
|
+
}
|
|
75
|
+
const token = ctx.get("x-lock");
|
|
76
|
+
if (!token) {
|
|
77
|
+
ctx.status = 403;
|
|
78
|
+
ctx.body = { error: "Blocked by x-lock: missing token" };
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const result = await enforce(apiUrl, siteKey, token, ctx.path);
|
|
83
|
+
if (result.blocked) {
|
|
84
|
+
ctx.status = 403;
|
|
85
|
+
ctx.body = { error: "Blocked by x-lock", reason: result.reason };
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (result.error && !failOpen) {
|
|
89
|
+
ctx.status = 403;
|
|
90
|
+
ctx.body = { error: "x-lock verification failed" };
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error("[x-lock] Enforcement error:", err.message);
|
|
95
|
+
if (!failOpen) {
|
|
96
|
+
ctx.status = 403;
|
|
97
|
+
ctx.body = { error: "x-lock verification failed" };
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return next();
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
105
|
+
0 && (module.exports = {
|
|
106
|
+
xlockMiddleware
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=koa.js.map
|
package/dist/koa.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/koa.ts","../src/core.ts"],"sourcesContent":["/**\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 * 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;AAKO,SAAS,eAAe,SAAsC;AACnE,SAAO,WAAW,QAAQ,IAAI;AAChC;;;ADlDO,SAAS,gBAAgB,UAA0B,CAAC,GAAG;AAC5D,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,QAAM,WAAW,QAAQ,aAAa;AACtC,QAAM,SAAS,QAAQ,UAAU,CAAC;AAElC,MAAI,CAAC,SAAS;AACZ,YAAQ,KAAK,6DAAwD;AACrE,WAAO,OAAO,MAAW,SAA8B,KAAK;AAAA,EAC9D;AAEA,SAAO,OAAO,KAAU,SAA8B;AACpD,QAAI,IAAI,WAAW,OAAQ,QAAO,KAAK;AAEvC,QAAI,OAAO,SAAS,KAAK,CAAC,OAAO,KAAK,CAAC,MAAc,IAAI,KAAK,WAAW,CAAC,CAAC,GAAG;AAC5E,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAAQ,IAAI,IAAI,QAAQ;AAE9B,QAAI,CAAC,OAAO;AACV,UAAI,SAAS;AACb,UAAI,OAAO,EAAE,OAAO,mCAAmC;AACvD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,SAAS,OAAO,IAAI,IAAI;AAE7D,UAAI,OAAO,SAAS;AAClB,YAAI,SAAS;AACb,YAAI,OAAO,EAAE,OAAO,qBAAqB,QAAQ,OAAO,OAAO;AAC/D;AAAA,MACF;AAEA,UAAI,OAAO,SAAS,CAAC,UAAU;AAC7B,YAAI,SAAS;AACb,YAAI,OAAO,EAAE,OAAO,6BAA6B;AACjD;AAAA,MACF;AAAA,IACF,SAAS,KAAU;AACjB,cAAQ,MAAM,+BAA+B,IAAI,OAAO;AACxD,UAAI,CAAC,UAAU;AACb,YAAI,SAAS;AACb,YAAI,OAAO,EAAE,OAAO,6BAA6B;AACjD;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AACF;","names":[]}
|
package/dist/koa.mjs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
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/koa.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
|
+
const routes = options.routes || [];
|
|
40
|
+
if (!siteKey) {
|
|
41
|
+
console.warn("[x-lock] No site key configured \u2014 skipping enforcement");
|
|
42
|
+
return async (_ctx, next) => next();
|
|
43
|
+
}
|
|
44
|
+
return async (ctx, next) => {
|
|
45
|
+
if (ctx.method !== "POST") return next();
|
|
46
|
+
if (routes.length > 0 && !routes.some((r) => ctx.path.startsWith(r))) {
|
|
47
|
+
return next();
|
|
48
|
+
}
|
|
49
|
+
const token = ctx.get("x-lock");
|
|
50
|
+
if (!token) {
|
|
51
|
+
ctx.status = 403;
|
|
52
|
+
ctx.body = { error: "Blocked by x-lock: missing token" };
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const result = await enforce(apiUrl, siteKey, token, ctx.path);
|
|
57
|
+
if (result.blocked) {
|
|
58
|
+
ctx.status = 403;
|
|
59
|
+
ctx.body = { error: "Blocked by x-lock", reason: result.reason };
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (result.error && !failOpen) {
|
|
63
|
+
ctx.status = 403;
|
|
64
|
+
ctx.body = { error: "x-lock verification failed" };
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error("[x-lock] Enforcement error:", err.message);
|
|
69
|
+
if (!failOpen) {
|
|
70
|
+
ctx.status = 403;
|
|
71
|
+
ctx.body = { error: "x-lock verification failed" };
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return next();
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
xlockMiddleware
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=koa.mjs.map
|
package/dist/koa.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/koa.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 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"],"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;AAKO,SAAS,eAAe,SAAsC;AACnE,SAAO,WAAW,QAAQ,IAAI;AAChC;;;AClDO,SAAS,gBAAgB,UAA0B,CAAC,GAAG;AAC5D,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,QAAM,WAAW,QAAQ,aAAa;AACtC,QAAM,SAAS,QAAQ,UAAU,CAAC;AAElC,MAAI,CAAC,SAAS;AACZ,YAAQ,KAAK,6DAAwD;AACrE,WAAO,OAAO,MAAW,SAA8B,KAAK;AAAA,EAC9D;AAEA,SAAO,OAAO,KAAU,SAA8B;AACpD,QAAI,IAAI,WAAW,OAAQ,QAAO,KAAK;AAEvC,QAAI,OAAO,SAAS,KAAK,CAAC,OAAO,KAAK,CAAC,MAAc,IAAI,KAAK,WAAW,CAAC,CAAC,GAAG;AAC5E,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAAQ,IAAI,IAAI,QAAQ;AAE9B,QAAI,CAAC,OAAO;AACV,UAAI,SAAS;AACb,UAAI,OAAO,EAAE,OAAO,mCAAmC;AACvD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,SAAS,OAAO,IAAI,IAAI;AAE7D,UAAI,OAAO,SAAS;AAClB,YAAI,SAAS;AACb,YAAI,OAAO,EAAE,OAAO,qBAAqB,QAAQ,OAAO,OAAO;AAC/D;AAAA,MACF;AAEA,UAAI,OAAO,SAAS,CAAC,UAAU;AAC7B,YAAI,SAAS;AACb,YAAI,OAAO,EAAE,OAAO,6BAA6B;AACjD;AAAA,MACF;AAAA,IACF,SAAS,KAAU;AACjB,cAAQ,MAAM,+BAA+B,IAAI,OAAO;AACxD,UAAI,CAAC,UAAU;AACb,YAAI,SAAS;AACb,YAAI,OAAO,EAAE,OAAO,6BAA6B;AACjD;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AACF;","names":[]}
|
package/dist/next.d.mts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { X as XLockConfig } from './core-D4eszYbi.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* x-lock middleware for Next.js
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // middleware.ts
|
|
9
|
+
* import { withXlock } from "@xlock/node/next";
|
|
10
|
+
* export const middleware = withXlock({
|
|
11
|
+
* siteKey: process.env.NEXT_PUBLIC_XLOCK_SITE_KEY!,
|
|
12
|
+
* protectedPaths: ["/api/auth/callback"],
|
|
13
|
+
* });
|
|
14
|
+
* export const config = { matcher: ["/api/:path*"] };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface XLockNextConfig extends XLockConfig {
|
|
19
|
+
/** Path prefixes to protect (POST only) */
|
|
20
|
+
protectedPaths: string[];
|
|
21
|
+
}
|
|
22
|
+
declare function withXlock(options: XLockNextConfig): (request: any) => Promise<any>;
|
|
23
|
+
|
|
24
|
+
export { type XLockNextConfig, withXlock };
|
package/dist/next.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { X as XLockConfig } from './core-D4eszYbi.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* x-lock middleware for Next.js
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // middleware.ts
|
|
9
|
+
* import { withXlock } from "@xlock/node/next";
|
|
10
|
+
* export const middleware = withXlock({
|
|
11
|
+
* siteKey: process.env.NEXT_PUBLIC_XLOCK_SITE_KEY!,
|
|
12
|
+
* protectedPaths: ["/api/auth/callback"],
|
|
13
|
+
* });
|
|
14
|
+
* export const config = { matcher: ["/api/:path*"] };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface XLockNextConfig extends XLockConfig {
|
|
19
|
+
/** Path prefixes to protect (POST only) */
|
|
20
|
+
protectedPaths: string[];
|
|
21
|
+
}
|
|
22
|
+
declare function withXlock(options: XLockNextConfig): (request: any) => Promise<any>;
|
|
23
|
+
|
|
24
|
+
export { type XLockNextConfig, withXlock };
|
package/dist/next.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
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/next.ts
|
|
21
|
+
var next_exports = {};
|
|
22
|
+
__export(next_exports, {
|
|
23
|
+
withXlock: () => withXlock
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(next_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/next.ts
|
|
58
|
+
function jsonForbidden(NextResponse, body) {
|
|
59
|
+
return NextResponse.json(body, { status: 403 });
|
|
60
|
+
}
|
|
61
|
+
function withXlock(options) {
|
|
62
|
+
const apiUrl = options.apiUrl || DEFAULT_API_URL;
|
|
63
|
+
const failOpen = options.failOpen !== false;
|
|
64
|
+
let _NextResponse;
|
|
65
|
+
return async function middleware(request) {
|
|
66
|
+
if (!_NextResponse) {
|
|
67
|
+
const mod = "next/server";
|
|
68
|
+
_NextResponse = (await import(mod)).NextResponse;
|
|
69
|
+
}
|
|
70
|
+
if (!options.siteKey) return _NextResponse.next();
|
|
71
|
+
if (request.method !== "POST") return _NextResponse.next();
|
|
72
|
+
const isProtected = options.protectedPaths.some(
|
|
73
|
+
(p) => request.nextUrl.pathname.startsWith(p)
|
|
74
|
+
);
|
|
75
|
+
if (!isProtected) return _NextResponse.next();
|
|
76
|
+
const token = request.headers.get("x-lock");
|
|
77
|
+
if (!token) {
|
|
78
|
+
return jsonForbidden(_NextResponse, { error: "Blocked by x-lock: missing token" });
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const result = await enforce(apiUrl, options.siteKey, token, request.nextUrl.pathname);
|
|
82
|
+
if (result.blocked) {
|
|
83
|
+
return jsonForbidden(_NextResponse, { error: "Blocked by x-lock", reason: result.reason });
|
|
84
|
+
}
|
|
85
|
+
if (result.error && !failOpen) {
|
|
86
|
+
return jsonForbidden(_NextResponse, { error: "x-lock verification failed" });
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error("[x-lock] Enforcement error:", err);
|
|
90
|
+
if (!failOpen) {
|
|
91
|
+
return jsonForbidden(_NextResponse, { error: "x-lock verification failed" });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return _NextResponse.next();
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
98
|
+
0 && (module.exports = {
|
|
99
|
+
withXlock
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=next.js.map
|
package/dist/next.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/next.ts","../src/core.ts"],"sourcesContent":["/**\n * x-lock middleware for Next.js\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { withXlock } from \"@xlock/node/next\";\n * export const middleware = withXlock({\n * siteKey: process.env.NEXT_PUBLIC_XLOCK_SITE_KEY!,\n * protectedPaths: [\"/api/auth/callback\"],\n * });\n * export const config = { matcher: [\"/api/:path*\"] };\n * ```\n */\n\nimport { enforce, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport interface XLockNextConfig extends XLockConfig {\n /** Path prefixes to protect (POST only) */\n protectedPaths: string[];\n}\n\n// Helper to create a JSON 403 response without importing next/server at build time\nfunction jsonForbidden(NextResponse: any, body: Record<string, unknown>) {\n return NextResponse.json(body, { status: 403 });\n}\n\nexport function withXlock(options: XLockNextConfig) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const failOpen = options.failOpen !== false;\n\n // next/server is resolved at runtime, not build time\n let _NextResponse: any;\n\n return async function middleware(request: any) {\n if (!_NextResponse) {\n // Dynamic require to avoid TS resolving next/server at build time\n const mod = \"next/server\";\n _NextResponse = (await import(mod)).NextResponse;\n }\n\n if (!options.siteKey) return _NextResponse.next();\n if (request.method !== \"POST\") return _NextResponse.next();\n\n const isProtected = options.protectedPaths.some((p: string) =>\n request.nextUrl.pathname.startsWith(p)\n );\n if (!isProtected) return _NextResponse.next();\n\n const token = request.headers.get(\"x-lock\");\n\n if (!token) {\n return jsonForbidden(_NextResponse, { error: \"Blocked by x-lock: missing token\" });\n }\n\n try {\n const result = await enforce(apiUrl, options.siteKey, token, request.nextUrl.pathname);\n\n if (result.blocked) {\n return jsonForbidden(_NextResponse, { error: \"Blocked by x-lock\", reason: result.reason });\n }\n\n if (result.error && !failOpen) {\n return jsonForbidden(_NextResponse, { error: \"x-lock verification failed\" });\n }\n } catch (err) {\n console.error(\"[x-lock] Enforcement error:\", err);\n if (!failOpen) {\n return jsonForbidden(_NextResponse, { error: \"x-lock verification failed\" });\n }\n }\n\n return _NextResponse.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;;;ADrCA,SAAS,cAAc,cAAmB,MAA+B;AACvE,SAAO,aAAa,KAAK,MAAM,EAAE,QAAQ,IAAI,CAAC;AAChD;AAEO,SAAS,UAAU,SAA0B;AAClD,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,WAAW,QAAQ,aAAa;AAGtC,MAAI;AAEJ,SAAO,eAAe,WAAW,SAAc;AAC7C,QAAI,CAAC,eAAe;AAElB,YAAM,MAAM;AACZ,uBAAiB,MAAM,OAAO,MAAM;AAAA,IACtC;AAEA,QAAI,CAAC,QAAQ,QAAS,QAAO,cAAc,KAAK;AAChD,QAAI,QAAQ,WAAW,OAAQ,QAAO,cAAc,KAAK;AAEzD,UAAM,cAAc,QAAQ,eAAe;AAAA,MAAK,CAAC,MAC/C,QAAQ,QAAQ,SAAS,WAAW,CAAC;AAAA,IACvC;AACA,QAAI,CAAC,YAAa,QAAO,cAAc,KAAK;AAE5C,UAAM,QAAQ,QAAQ,QAAQ,IAAI,QAAQ;AAE1C,QAAI,CAAC,OAAO;AACV,aAAO,cAAc,eAAe,EAAE,OAAO,mCAAmC,CAAC;AAAA,IACnF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,QAAQ,SAAS,OAAO,QAAQ,QAAQ,QAAQ;AAErF,UAAI,OAAO,SAAS;AAClB,eAAO,cAAc,eAAe,EAAE,OAAO,qBAAqB,QAAQ,OAAO,OAAO,CAAC;AAAA,MAC3F;AAEA,UAAI,OAAO,SAAS,CAAC,UAAU;AAC7B,eAAO,cAAc,eAAe,EAAE,OAAO,6BAA6B,CAAC;AAAA,MAC7E;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,+BAA+B,GAAG;AAChD,UAAI,CAAC,UAAU;AACb,eAAO,cAAc,eAAe,EAAE,OAAO,6BAA6B,CAAC;AAAA,MAC7E;AAAA,IACF;AAEA,WAAO,cAAc,KAAK;AAAA,EAC5B;AACF;","names":[]}
|
package/dist/next.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
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/next.ts
|
|
32
|
+
function jsonForbidden(NextResponse, body) {
|
|
33
|
+
return NextResponse.json(body, { status: 403 });
|
|
34
|
+
}
|
|
35
|
+
function withXlock(options) {
|
|
36
|
+
const apiUrl = options.apiUrl || DEFAULT_API_URL;
|
|
37
|
+
const failOpen = options.failOpen !== false;
|
|
38
|
+
let _NextResponse;
|
|
39
|
+
return async function middleware(request) {
|
|
40
|
+
if (!_NextResponse) {
|
|
41
|
+
const mod = "next/server";
|
|
42
|
+
_NextResponse = (await import(mod)).NextResponse;
|
|
43
|
+
}
|
|
44
|
+
if (!options.siteKey) return _NextResponse.next();
|
|
45
|
+
if (request.method !== "POST") return _NextResponse.next();
|
|
46
|
+
const isProtected = options.protectedPaths.some(
|
|
47
|
+
(p) => request.nextUrl.pathname.startsWith(p)
|
|
48
|
+
);
|
|
49
|
+
if (!isProtected) return _NextResponse.next();
|
|
50
|
+
const token = request.headers.get("x-lock");
|
|
51
|
+
if (!token) {
|
|
52
|
+
return jsonForbidden(_NextResponse, { error: "Blocked by x-lock: missing token" });
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const result = await enforce(apiUrl, options.siteKey, token, request.nextUrl.pathname);
|
|
56
|
+
if (result.blocked) {
|
|
57
|
+
return jsonForbidden(_NextResponse, { error: "Blocked by x-lock", reason: result.reason });
|
|
58
|
+
}
|
|
59
|
+
if (result.error && !failOpen) {
|
|
60
|
+
return jsonForbidden(_NextResponse, { error: "x-lock verification failed" });
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error("[x-lock] Enforcement error:", err);
|
|
64
|
+
if (!failOpen) {
|
|
65
|
+
return jsonForbidden(_NextResponse, { error: "x-lock verification failed" });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return _NextResponse.next();
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
withXlock
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=next.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/next.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 Next.js\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { withXlock } from \"@xlock/node/next\";\n * export const middleware = withXlock({\n * siteKey: process.env.NEXT_PUBLIC_XLOCK_SITE_KEY!,\n * protectedPaths: [\"/api/auth/callback\"],\n * });\n * export const config = { matcher: [\"/api/:path*\"] };\n * ```\n */\n\nimport { enforce, DEFAULT_API_URL, type XLockConfig } from \"./core\";\n\nexport interface XLockNextConfig extends XLockConfig {\n /** Path prefixes to protect (POST only) */\n protectedPaths: string[];\n}\n\n// Helper to create a JSON 403 response without importing next/server at build time\nfunction jsonForbidden(NextResponse: any, body: Record<string, unknown>) {\n return NextResponse.json(body, { status: 403 });\n}\n\nexport function withXlock(options: XLockNextConfig) {\n const apiUrl = options.apiUrl || DEFAULT_API_URL;\n const failOpen = options.failOpen !== false;\n\n // next/server is resolved at runtime, not build time\n let _NextResponse: any;\n\n return async function middleware(request: any) {\n if (!_NextResponse) {\n // Dynamic require to avoid TS resolving next/server at build time\n const mod = \"next/server\";\n _NextResponse = (await import(mod)).NextResponse;\n }\n\n if (!options.siteKey) return _NextResponse.next();\n if (request.method !== \"POST\") return _NextResponse.next();\n\n const isProtected = options.protectedPaths.some((p: string) =>\n request.nextUrl.pathname.startsWith(p)\n );\n if (!isProtected) return _NextResponse.next();\n\n const token = request.headers.get(\"x-lock\");\n\n if (!token) {\n return jsonForbidden(_NextResponse, { error: \"Blocked by x-lock: missing token\" });\n }\n\n try {\n const result = await enforce(apiUrl, options.siteKey, token, request.nextUrl.pathname);\n\n if (result.blocked) {\n return jsonForbidden(_NextResponse, { error: \"Blocked by x-lock\", reason: result.reason });\n }\n\n if (result.error && !failOpen) {\n return jsonForbidden(_NextResponse, { error: \"x-lock verification failed\" });\n }\n } catch (err) {\n console.error(\"[x-lock] Enforcement error:\", err);\n if (!failOpen) {\n return jsonForbidden(_NextResponse, { error: \"x-lock verification failed\" });\n }\n }\n\n return _NextResponse.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;;;ACrCA,SAAS,cAAc,cAAmB,MAA+B;AACvE,SAAO,aAAa,KAAK,MAAM,EAAE,QAAQ,IAAI,CAAC;AAChD;AAEO,SAAS,UAAU,SAA0B;AAClD,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,WAAW,QAAQ,aAAa;AAGtC,MAAI;AAEJ,SAAO,eAAe,WAAW,SAAc;AAC7C,QAAI,CAAC,eAAe;AAElB,YAAM,MAAM;AACZ,uBAAiB,MAAM,OAAO,MAAM;AAAA,IACtC;AAEA,QAAI,CAAC,QAAQ,QAAS,QAAO,cAAc,KAAK;AAChD,QAAI,QAAQ,WAAW,OAAQ,QAAO,cAAc,KAAK;AAEzD,UAAM,cAAc,QAAQ,eAAe;AAAA,MAAK,CAAC,MAC/C,QAAQ,QAAQ,SAAS,WAAW,CAAC;AAAA,IACvC;AACA,QAAI,CAAC,YAAa,QAAO,cAAc,KAAK;AAE5C,UAAM,QAAQ,QAAQ,QAAQ,IAAI,QAAQ;AAE1C,QAAI,CAAC,OAAO;AACV,aAAO,cAAc,eAAe,EAAE,OAAO,mCAAmC,CAAC;AAAA,IACnF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,QAAQ,SAAS,OAAO,QAAQ,QAAQ,QAAQ;AAErF,UAAI,OAAO,SAAS;AAClB,eAAO,cAAc,eAAe,EAAE,OAAO,qBAAqB,QAAQ,OAAO,OAAO,CAAC;AAAA,MAC3F;AAEA,UAAI,OAAO,SAAS,CAAC,UAAU;AAC7B,eAAO,cAAc,eAAe,EAAE,OAAO,6BAA6B,CAAC;AAAA,MAC7E;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,+BAA+B,GAAG;AAChD,UAAI,CAAC,UAAU;AACb,eAAO,cAAc,eAAe,EAAE,OAAO,6BAA6B,CAAC;AAAA,MAC7E;AAAA,IACF;AAEA,WAAO,cAAc,KAAK;AAAA,EAC5B;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xlock/node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "x-lock bot protection — invisible middleware for Express, Fastify, Koa, Next.js, and Hono",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./express": {
|
|
15
|
+
"types": "./dist/express.d.ts",
|
|
16
|
+
"import": "./dist/express.mjs",
|
|
17
|
+
"require": "./dist/express.js"
|
|
18
|
+
},
|
|
19
|
+
"./fastify": {
|
|
20
|
+
"types": "./dist/fastify.d.ts",
|
|
21
|
+
"import": "./dist/fastify.mjs",
|
|
22
|
+
"require": "./dist/fastify.js"
|
|
23
|
+
},
|
|
24
|
+
"./koa": {
|
|
25
|
+
"types": "./dist/koa.d.ts",
|
|
26
|
+
"import": "./dist/koa.mjs",
|
|
27
|
+
"require": "./dist/koa.js"
|
|
28
|
+
},
|
|
29
|
+
"./next": {
|
|
30
|
+
"types": "./dist/next.d.ts",
|
|
31
|
+
"import": "./dist/next.mjs",
|
|
32
|
+
"require": "./dist/next.js"
|
|
33
|
+
},
|
|
34
|
+
"./hono": {
|
|
35
|
+
"types": "./dist/hono.d.ts",
|
|
36
|
+
"import": "./dist/hono.mjs",
|
|
37
|
+
"require": "./dist/hono.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"README.md"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsup",
|
|
47
|
+
"prepublishOnly": "npm run build"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"xlock",
|
|
51
|
+
"x-lock",
|
|
52
|
+
"bot-protection",
|
|
53
|
+
"captcha",
|
|
54
|
+
"captcha-alternative",
|
|
55
|
+
"middleware",
|
|
56
|
+
"express",
|
|
57
|
+
"fastify",
|
|
58
|
+
"nextjs",
|
|
59
|
+
"hono",
|
|
60
|
+
"koa",
|
|
61
|
+
"proof-of-work",
|
|
62
|
+
"recaptcha-alternative",
|
|
63
|
+
"turnstile-alternative"
|
|
64
|
+
],
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "https://github.com/x-lock-dev/xlock-node"
|
|
69
|
+
},
|
|
70
|
+
"homepage": "https://x-lock.dev",
|
|
71
|
+
"bugs": {
|
|
72
|
+
"url": "https://github.com/x-lock-dev/xlock-node/issues"
|
|
73
|
+
},
|
|
74
|
+
"engines": {
|
|
75
|
+
"node": ">=18.0.0"
|
|
76
|
+
},
|
|
77
|
+
"peerDependencies": {
|
|
78
|
+
"next": ">=13.0.0",
|
|
79
|
+
"hono": ">=3.0.0",
|
|
80
|
+
"koa": ">=2.0.0",
|
|
81
|
+
"fastify": ">=4.0.0"
|
|
82
|
+
},
|
|
83
|
+
"peerDependenciesMeta": {
|
|
84
|
+
"next": { "optional": true },
|
|
85
|
+
"hono": { "optional": true },
|
|
86
|
+
"koa": { "optional": true },
|
|
87
|
+
"fastify": { "optional": true }
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"tsup": "^8.0.0",
|
|
91
|
+
"typescript": "^5.0.0",
|
|
92
|
+
"@types/node": "^20.0.0"
|
|
93
|
+
}
|
|
94
|
+
}
|