@kcaptcha/express 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/dist/index.d.ts +24 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +19 -0
- package/dist/middleware.js +59 -0
- package/dist/middleware.js.map +1 -0
- package/dist/routes.d.ts +7 -0
- package/dist/routes.js +156 -0
- package/dist/routes.js.map +1 -0
- package/package.json +35 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RequestHandler, Router } from 'express';
|
|
2
|
+
import type { KCaptchaOptions, ResolvedConfig } from '@kcaptcha/core';
|
|
3
|
+
import type { RequireKCaptchaOptions } from './middleware.js';
|
|
4
|
+
export interface ExpressKCaptchaOptions extends KCaptchaOptions {
|
|
5
|
+
/** Serve the browser widget at <endpoint>/kcaptcha.js. Default true. */
|
|
6
|
+
serveClient?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface KCaptchaInstance {
|
|
9
|
+
/** Mount this at your endpoint, e.g. app.use('/login/kcaptcha', kc.router). */
|
|
10
|
+
readonly router: Router;
|
|
11
|
+
/** Build a guard for your own routes. Consumes the pass token (single use). */
|
|
12
|
+
readonly requireKCaptcha: (options?: RequireKCaptchaOptions) => RequestHandler;
|
|
13
|
+
/** The resolved configuration (secret, expire, rate limit, store, ...). */
|
|
14
|
+
readonly config: ResolvedConfig;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a kCAPTCHA instance. Throws KCaptchaConfigError right away if the
|
|
18
|
+
* configuration is invalid (for example a missing or short "secret"), so a
|
|
19
|
+
* misconfigured server fails at startup rather than at the first request.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createKCaptcha(options: ExpressKCaptchaOptions): KCaptchaInstance;
|
|
22
|
+
export type { RequireKCaptchaOptions, RequireFailure } from './middleware.js';
|
|
23
|
+
export { KCaptchaConfigError, MemoryStore, RedisStore } from '@kcaptcha/core';
|
|
24
|
+
export type { KCaptchaOptions, KCaptchaStore } from '@kcaptcha/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { resolveConfig } from '@kcaptcha/core';
|
|
2
|
+
import { makeRequireKCaptcha } from './middleware.js';
|
|
3
|
+
import { buildRouter } from './routes.js';
|
|
4
|
+
/**
|
|
5
|
+
* Create a kCAPTCHA instance. Throws KCaptchaConfigError right away if the
|
|
6
|
+
* configuration is invalid (for example a missing or short "secret"), so a
|
|
7
|
+
* misconfigured server fails at startup rather than at the first request.
|
|
8
|
+
*/
|
|
9
|
+
export function createKCaptcha(options) {
|
|
10
|
+
const config = resolveConfig(options);
|
|
11
|
+
const router = buildRouter(config, { serveClient: options.serveClient ?? true });
|
|
12
|
+
return {
|
|
13
|
+
router,
|
|
14
|
+
requireKCaptcha: makeRequireKCaptcha(config),
|
|
15
|
+
config,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// Convenience re-exports so most apps import from a single package.
|
|
19
|
+
export { KCaptchaConfigError, MemoryStore, RedisStore } from '@kcaptcha/core';
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgB1C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAA+B;IAC5D,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC,CAAC;IACjF,OAAO;QACL,MAAM;QACN,eAAe,EAAE,mBAAmB,CAAC,MAAM,CAAC;QAC5C,MAAM;KACP,CAAC;AACJ,CAAC;AAID,oEAAoE;AACpE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
2
|
+
import type { PassFailure, ResolvedConfig } from '@kcaptcha/core';
|
|
3
|
+
export type RequireFailure = 'missing' | PassFailure;
|
|
4
|
+
export interface RequireKCaptchaOptions {
|
|
5
|
+
/** Body field that carries the pass token. Default "kcaptcha_token". */
|
|
6
|
+
bodyField?: string;
|
|
7
|
+
/** Header that may carry the pass token instead. Default "x-kcaptcha-token". */
|
|
8
|
+
headerName?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Custom rejection handler. Default: 403 JSON { ok: false, error: "captcha_..." }.
|
|
11
|
+
* Call res/next yourself; the request is NOT continued automatically.
|
|
12
|
+
*/
|
|
13
|
+
onFail?: (req: Request, res: Response, next: NextFunction, reason: RequireFailure) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Builds the protection middleware bound to ONE shared config, so pass tokens
|
|
17
|
+
* consumed here are burned in the same store the router uses.
|
|
18
|
+
*/
|
|
19
|
+
export declare function makeRequireKCaptcha(config: ResolvedConfig): (options?: RequireKCaptchaOptions) => RequestHandler;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { consumePass } from '@kcaptcha/core';
|
|
2
|
+
const ERROR_CODE = {
|
|
3
|
+
missing: 'captcha_required',
|
|
4
|
+
malformed: 'captcha_invalid',
|
|
5
|
+
invalid: 'captcha_invalid',
|
|
6
|
+
expired: 'captcha_expired',
|
|
7
|
+
replayed: 'captcha_replayed',
|
|
8
|
+
};
|
|
9
|
+
function extractToken(req, bodyField, headerName) {
|
|
10
|
+
const header = req.get(headerName);
|
|
11
|
+
if (typeof header === 'string' && header.length > 0)
|
|
12
|
+
return header;
|
|
13
|
+
const body = req.body;
|
|
14
|
+
if (typeof body === 'object' && body !== null) {
|
|
15
|
+
const value = body[bodyField];
|
|
16
|
+
if (typeof value === 'string' && value.length > 0)
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Builds the protection middleware bound to ONE shared config, so pass tokens
|
|
23
|
+
* consumed here are burned in the same store the router uses.
|
|
24
|
+
*/
|
|
25
|
+
export function makeRequireKCaptcha(config) {
|
|
26
|
+
return function requireKCaptcha(options = {}) {
|
|
27
|
+
const bodyField = options.bodyField ?? 'kcaptcha_token';
|
|
28
|
+
const headerName = options.headerName ?? 'x-kcaptcha-token';
|
|
29
|
+
const reject = (req, res, next, reason) => {
|
|
30
|
+
if (options.onFail) {
|
|
31
|
+
options.onFail(req, res, next, reason);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
res.status(403).json({ ok: false, error: ERROR_CODE[reason] });
|
|
35
|
+
};
|
|
36
|
+
return async (req, res, next) => {
|
|
37
|
+
const token = extractToken(req, bodyField, headerName);
|
|
38
|
+
if (token === undefined) {
|
|
39
|
+
reject(req, res, next, 'missing');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const result = await consumePass(config, token);
|
|
44
|
+
if (!result.ok) {
|
|
45
|
+
reject(req, res, next, result.reason);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
res.locals.kcaptcha = { verified: true };
|
|
49
|
+
next();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
// Store failure: fail closed.
|
|
53
|
+
console.error('[kCAPTCHA] Could not verify pass token (failing closed):', err);
|
|
54
|
+
res.status(503).json({ ok: false, error: 'service_unavailable' });
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAiB7C,MAAM,UAAU,GAAmC;IACjD,OAAO,EAAE,kBAAkB;IAC3B,SAAS,EAAE,iBAAiB;IAC5B,OAAO,EAAE,iBAAiB;IAC1B,OAAO,EAAE,iBAAiB;IAC1B,QAAQ,EAAE,kBAAkB;CAC7B,CAAC;AAEF,SAAS,YAAY,CAAC,GAAY,EAAE,SAAiB,EAAE,UAAkB;IACvE,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAEnE,MAAM,IAAI,GAAY,GAAG,CAAC,IAAI,CAAC;IAC/B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAI,IAAgC,CAAC,SAAS,CAAC,CAAC;QAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAsB;IACxD,OAAO,SAAS,eAAe,CAAC,UAAkC,EAAE;QAClE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,gBAAgB,CAAC;QACxD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,CAAC;QAE5D,MAAM,MAAM,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,MAAsB,EAAE,EAAE;YACzF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;gBACvC,OAAO;YACT,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjE,CAAC,CAAC;QAEF,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAC9B,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;YACvD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBAClC,OAAO;YACT,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBAChD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;oBACtC,OAAO;gBACT,CAAC;gBACD,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACzC,IAAI,EAAE,CAAC;YACT,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,8BAA8B;gBAC9B,OAAO,CAAC,KAAK,CAAC,0DAA0D,EAAE,GAAG,CAAC,CAAC;gBAC/E,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;YACpE,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Router } from 'express';
|
|
2
|
+
import type { ResolvedConfig } from '@kcaptcha/core';
|
|
3
|
+
export interface RoutesOptions {
|
|
4
|
+
/** Serve the browser widget at <endpoint>/kcaptcha.js. */
|
|
5
|
+
serveClient: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function buildRouter(config: ResolvedConfig, options: RoutesOptions): Router;
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import { checkRateLimit, createChallenge, verifyAnswer } from '@kcaptcha/core';
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
// verify-captcha bodies are tiny: { token, answer }.
|
|
6
|
+
const BODY_LIMIT = '2kb';
|
|
7
|
+
const VERIFY_STATUS = {
|
|
8
|
+
malformed: 400,
|
|
9
|
+
invalid: 400,
|
|
10
|
+
expired: 410,
|
|
11
|
+
replayed: 409,
|
|
12
|
+
incorrect: 403,
|
|
13
|
+
};
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Small helpers
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
function fail(res, status, error, extra = {}) {
|
|
18
|
+
res.status(status).json({ ok: false, error, ...extra });
|
|
19
|
+
}
|
|
20
|
+
/** Store or internal failure: log for the operator, tell the client nothing useful, fail closed. */
|
|
21
|
+
function unavailable(res, err) {
|
|
22
|
+
console.error('[kCAPTCHA] Store or internal error (failing closed):', err);
|
|
23
|
+
if (!res.headersSent)
|
|
24
|
+
fail(res, 503, 'service_unavailable');
|
|
25
|
+
}
|
|
26
|
+
function isRecord(value) {
|
|
27
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
// req.ip honours Express's "trust proxy" setting. Behind a reverse proxy you
|
|
30
|
+
// MUST configure it (app.set('trust proxy', ...)) or every visitor shares one
|
|
31
|
+
// rate-limit bucket.
|
|
32
|
+
function clientIp(req) {
|
|
33
|
+
return req.ip ?? req.socket.remoteAddress ?? 'unknown';
|
|
34
|
+
}
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Shared middleware
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
const securityHeaders = (_req, res, next) => {
|
|
39
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
40
|
+
next();
|
|
41
|
+
};
|
|
42
|
+
// Challenges and passes must never be cached by browsers or proxies.
|
|
43
|
+
const noStore = (_req, res, next) => {
|
|
44
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
45
|
+
next();
|
|
46
|
+
};
|
|
47
|
+
function methodNotAllowed(allow) {
|
|
48
|
+
return (_req, res) => {
|
|
49
|
+
res.setHeader('Allow', allow);
|
|
50
|
+
fail(res, 405, 'method_not_allowed');
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function rateLimit(config, bucket) {
|
|
54
|
+
return async (req, res, next) => {
|
|
55
|
+
try {
|
|
56
|
+
const result = await checkRateLimit(config, bucket, clientIp(req));
|
|
57
|
+
res.setHeader('RateLimit-Limit', String(result.limit));
|
|
58
|
+
res.setHeader('RateLimit-Remaining', String(result.remaining));
|
|
59
|
+
if (!result.allowed) {
|
|
60
|
+
res.setHeader('Retry-After', String(result.retryAfter));
|
|
61
|
+
fail(res, 429, 'rate_limited', { retryAfter: result.retryAfter });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
next();
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
unavailable(res, err);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Serving the browser bundle from the site's own server
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
let cachedBundlePath;
|
|
75
|
+
function resolveBundlePath() {
|
|
76
|
+
if (cachedBundlePath === undefined) {
|
|
77
|
+
try {
|
|
78
|
+
cachedBundlePath = require.resolve('@kcaptcha/client/kcaptcha.js');
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
cachedBundlePath = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return cachedBundlePath;
|
|
85
|
+
}
|
|
86
|
+
const serveClientBundle = (_req, res) => {
|
|
87
|
+
const bundle = resolveBundlePath();
|
|
88
|
+
if (!bundle) {
|
|
89
|
+
console.error('[kCAPTCHA] Client bundle not found. Build it with "npm run build" ' +
|
|
90
|
+
'or set serveClient: false and host kcaptcha.js yourself.');
|
|
91
|
+
res.status(500).type('text/plain').send('kCAPTCHA client bundle is not available.');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// sendFile provides ETag / Last-Modified / 304 handling.
|
|
95
|
+
res.sendFile(bundle, { maxAge: process.env.NODE_ENV === 'production' ? '1h' : 0 }, (err) => {
|
|
96
|
+
if (err) {
|
|
97
|
+
console.error('[kCAPTCHA] Failed to send client bundle:', err);
|
|
98
|
+
if (!res.headersSent)
|
|
99
|
+
res.status(500).type('text/plain').send('Failed to load kCAPTCHA.');
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// The router
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
export function buildRouter(config, options) {
|
|
107
|
+
const router = express.Router();
|
|
108
|
+
router.use(securityHeaders);
|
|
109
|
+
if (options.serveClient) {
|
|
110
|
+
router.get('/kcaptcha.js', serveClientBundle);
|
|
111
|
+
}
|
|
112
|
+
// POST <endpoint>/new-code
|
|
113
|
+
router
|
|
114
|
+
.route('/new-code')
|
|
115
|
+
.post(noStore, rateLimit(config, 'new-code'), (_req, res) => {
|
|
116
|
+
try {
|
|
117
|
+
res.status(200).json({ ok: true, ...createChallenge(config) });
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
unavailable(res, err);
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
.all(methodNotAllowed('POST'));
|
|
124
|
+
// POST <endpoint>/verify-captcha body: { token, answer }
|
|
125
|
+
router
|
|
126
|
+
.route('/verify-captcha')
|
|
127
|
+
.post(noStore, rateLimit(config, 'verify-captcha'), // rate limit first: cheap rejection before body parsing
|
|
128
|
+
express.json({ limit: BODY_LIMIT, strict: true, type: 'application/json' }), async (req, res) => {
|
|
129
|
+
try {
|
|
130
|
+
const body = req.body;
|
|
131
|
+
const fields = isRecord(body) ? body : {};
|
|
132
|
+
const result = await verifyAnswer(config, fields.token, fields.answer);
|
|
133
|
+
if (result.ok) {
|
|
134
|
+
res.status(200).json({ ok: true, token: result.pass, expiresIn: result.expiresIn });
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
fail(res, VERIFY_STATUS[result.reason], result.reason);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
unavailable(res, err);
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
.all(methodNotAllowed('POST'));
|
|
145
|
+
// Body-parser errors (bad JSON, oversized body) -> clean JSON instead of an HTML stack page.
|
|
146
|
+
router.use((err, _req, res, next) => {
|
|
147
|
+
const status = isRecord(err) && typeof err.status === 'number' ? err.status : 0;
|
|
148
|
+
if (status >= 400 && status < 500) {
|
|
149
|
+
fail(res, status === 413 ? 413 : 400, status === 413 ? 'payload_too_large' : 'bad_request');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
next(err);
|
|
153
|
+
});
|
|
154
|
+
return router;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kcaptcha/express",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Express adapter for kCAPTCHA: router, client bundle hosting and route protection",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@kcaptcha/client": "^0.1.0",
|
|
22
|
+
"@kcaptcha/core": "^0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"express": "^4.18.0 || ^5.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/express": "^4.17.21",
|
|
29
|
+
"@types/supertest": "^6.0.0",
|
|
30
|
+
"supertest": "^7.0.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
}
|
|
35
|
+
}
|