@voyantjs/hono 0.31.3 → 0.32.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 CHANGED
@@ -6,6 +6,8 @@ export { consoleLoggerProvider, cors, DEFAULT_IDEMPOTENCY_TTL_MS, db, errorBound
6
6
  export type { HonoExtension, HonoModule } from "./module.js";
7
7
  export type { ExpandedHonoBundles, ExpandedHonoPlugins, HonoBundle, HonoPlugin, } from "./plugin.js";
8
8
  export { defineHonoBundle, defineHonoPlugin, expandHonoBundles, expandHonoPlugins, } from "./plugin.js";
9
+ export type { CreatePublicCapabilityOptions, PublicCapabilityCookieOptions, PublicCapabilityPayload, VerifyPublicCapabilityOptions, } from "./public-capability.js";
10
+ export { createPublicCapabilityToken, extractPublicCapabilityToken, serializePublicCapabilityCookie, verifyPublicCapabilityToken, } from "./public-capability.js";
9
11
  export type { DbFactory, LogEntry, LoggerProvider, VoyantAppConfig, VoyantAuthIntegration, VoyantAuthPermissionArgs, VoyantAuthResolveArgs, VoyantBindings, VoyantDb, VoyantExecutionContext, VoyantQueryRuntime, VoyantRequestAuthContext, VoyantVariables, } from "./types.js";
10
12
  export { ApiHttpError, ForbiddenApiError, normalizeValidationError, parseJsonBody, parseOptionalJsonBody, parseQuery, RequestValidationError, UnauthorizedApiError, } from "./validation.js";
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,qBAAqB,EACrB,IAAI,EACJ,0BAA0B,EAC1B,EAAE,EACF,aAAa,EACb,cAAc,EACd,KAAK,qBAAqB,EAC1B,cAAc,EACd,WAAW,EACX,MAAM,EACN,2BAA2B,EAC3B,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC5D,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,EACd,QAAQ,EACR,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,qBAAqB,EACrB,IAAI,EACJ,0BAA0B,EAC1B,EAAE,EACF,aAAa,EACb,cAAc,EACd,KAAK,qBAAqB,EAC1B,cAAc,EACd,WAAW,EACX,MAAM,EACN,2BAA2B,EAC3B,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC5D,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,6BAA6B,EAC7B,6BAA6B,EAC7B,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EACV,SAAS,EACT,QAAQ,EACR,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,cAAc,EACd,QAAQ,EACR,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,iBAAiB,CAAA"}
package/dist/index.js CHANGED
@@ -2,4 +2,5 @@ export { createApp } from "./app.js";
2
2
  export { extractBearerToken, generateNumericCode, randomBytesHex, requireUserId, sha256Base64Url, sha256Hex, unsignCookie, verifySession, } from "./auth/index.js";
3
3
  export { consoleLoggerProvider, cors, DEFAULT_IDEMPOTENCY_TTL_MS, db, errorBoundary, handleApiError, idempotencyKey, LIVE_LIMITS, logger, purgeExpiredIdempotencyKeys, rateLimit, requestId, requireActor, requireAuth, requirePermission, } from "./middleware/index.js";
4
4
  export { defineHonoBundle, defineHonoPlugin, expandHonoBundles, expandHonoPlugins, } from "./plugin.js";
5
+ export { createPublicCapabilityToken, extractPublicCapabilityToken, serializePublicCapabilityCookie, verifyPublicCapabilityToken, } from "./public-capability.js";
5
6
  export { ApiHttpError, ForbiddenApiError, normalizeValidationError, parseJsonBody, parseOptionalJsonBody, parseQuery, RequestValidationError, UnauthorizedApiError, } from "./validation.js";
@@ -0,0 +1,46 @@
1
+ import type { Context } from "hono";
2
+ export interface PublicCapabilityPayload {
3
+ v: 1;
4
+ scope: string;
5
+ subjectId: string;
6
+ actions: string[];
7
+ iat: number;
8
+ exp: number;
9
+ jti?: string;
10
+ }
11
+ export interface CreatePublicCapabilityOptions {
12
+ secret: string;
13
+ scope: string;
14
+ subjectId: string;
15
+ actions: string[];
16
+ ttlSeconds: number;
17
+ now?: Date;
18
+ jti?: string;
19
+ }
20
+ export interface VerifyPublicCapabilityOptions {
21
+ secret: string;
22
+ scope: string;
23
+ subjectId: string;
24
+ action: string;
25
+ now?: Date;
26
+ }
27
+ export interface PublicCapabilityCookieOptions {
28
+ name: string;
29
+ token: string;
30
+ expiresAt: Date;
31
+ secure?: boolean;
32
+ sameSite?: "Strict" | "Lax" | "None";
33
+ path?: string;
34
+ }
35
+ export declare function createPublicCapabilityToken(options: CreatePublicCapabilityOptions): Promise<{
36
+ token: string;
37
+ payload: PublicCapabilityPayload;
38
+ expiresAt: Date;
39
+ }>;
40
+ export declare function verifyPublicCapabilityToken(token: string, options: VerifyPublicCapabilityOptions): Promise<PublicCapabilityPayload>;
41
+ export declare function extractPublicCapabilityToken(c: Context, options?: {
42
+ headerName?: string;
43
+ cookieName?: string;
44
+ }): string | null;
45
+ export declare function serializePublicCapabilityCookie(options: PublicCapabilityCookieOptions): string;
46
+ //# sourceMappingURL=public-capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-capability.d.ts","sourceRoot":"","sources":["../src/public-capability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAInC,MAAM,WAAW,uBAAuB;IACtC,CAAC,EAAE,CAAC,CAAA;IACJ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,IAAI,CAAA;CACX;AAED,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,IAAI,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAA;IACpC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AA2DD,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,uBAAuB,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAyB/E;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CAyClC;AAED,wBAAgB,4BAA4B,CAC1C,CAAC,EAAE,OAAO,EACV,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,iBA0B3D;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,6BAA6B,UAiBrF"}
@@ -0,0 +1,140 @@
1
+ import { ForbiddenApiError, UnauthorizedApiError } from "./validation.js";
2
+ const TOKEN_TYPE = "voyant-public-capability+jwt";
3
+ const DEFAULT_CAPABILITY_HEADER = "X-Voyant-Checkout-Capability";
4
+ function getWebCrypto() {
5
+ if (globalThis.crypto?.subtle) {
6
+ return globalThis.crypto;
7
+ }
8
+ throw new Error("No crypto implementation available");
9
+ }
10
+ function encodeBase64Url(input) {
11
+ const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
12
+ let binary = "";
13
+ for (const byte of bytes) {
14
+ binary += String.fromCharCode(byte);
15
+ }
16
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
17
+ }
18
+ function decodeBase64Url(input) {
19
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
20
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
21
+ return atob(padded);
22
+ }
23
+ function constantTimeEqual(left, right) {
24
+ if (left.length !== right.length)
25
+ return false;
26
+ let result = 0;
27
+ for (let index = 0; index < left.length; index += 1) {
28
+ result |= left.charCodeAt(index) ^ right.charCodeAt(index);
29
+ }
30
+ return result === 0;
31
+ }
32
+ async function signMessage(message, secret) {
33
+ const crypto = getWebCrypto();
34
+ const encoder = new TextEncoder();
35
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
36
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
37
+ return encodeBase64Url(new Uint8Array(signature));
38
+ }
39
+ function validateSecret(secret) {
40
+ if (secret.length < 32) {
41
+ throw new Error("Public capability secret must be at least 32 characters");
42
+ }
43
+ }
44
+ export async function createPublicCapabilityToken(options) {
45
+ validateSecret(options.secret);
46
+ const issuedAt = Math.floor((options.now ?? new Date()).getTime() / 1000);
47
+ const expiresAtSeconds = issuedAt + options.ttlSeconds;
48
+ const payload = {
49
+ v: 1,
50
+ scope: options.scope,
51
+ subjectId: options.subjectId,
52
+ actions: Array.from(new Set(options.actions)).sort(),
53
+ iat: issuedAt,
54
+ exp: expiresAtSeconds,
55
+ ...(options.jti ? { jti: options.jti } : {}),
56
+ };
57
+ const header = { alg: "HS256", typ: TOKEN_TYPE };
58
+ const headerB64 = encodeBase64Url(JSON.stringify(header));
59
+ const payloadB64 = encodeBase64Url(JSON.stringify(payload));
60
+ const message = `${headerB64}.${payloadB64}`;
61
+ const signature = await signMessage(message, options.secret);
62
+ return {
63
+ token: `${message}.${signature}`,
64
+ payload,
65
+ expiresAt: new Date(expiresAtSeconds * 1000),
66
+ };
67
+ }
68
+ export async function verifyPublicCapabilityToken(token, options) {
69
+ validateSecret(options.secret);
70
+ const [headerB64, payloadB64, signature] = token.split(".");
71
+ if (!headerB64 || !payloadB64 || !signature) {
72
+ throw new UnauthorizedApiError("Invalid checkout session capability");
73
+ }
74
+ const message = `${headerB64}.${payloadB64}`;
75
+ const expectedSignature = await signMessage(message, options.secret);
76
+ if (!constantTimeEqual(signature, expectedSignature)) {
77
+ throw new UnauthorizedApiError("Invalid checkout session capability");
78
+ }
79
+ let header;
80
+ let payload;
81
+ try {
82
+ header = JSON.parse(decodeBase64Url(headerB64));
83
+ payload = JSON.parse(decodeBase64Url(payloadB64));
84
+ }
85
+ catch {
86
+ throw new UnauthorizedApiError("Invalid checkout session capability");
87
+ }
88
+ if (header.typ !== TOKEN_TYPE || payload.v !== 1) {
89
+ throw new UnauthorizedApiError("Invalid checkout session capability");
90
+ }
91
+ const now = Math.floor((options.now ?? new Date()).getTime() / 1000);
92
+ if (payload.exp < now) {
93
+ throw new UnauthorizedApiError("Expired checkout session capability");
94
+ }
95
+ if (payload.scope !== options.scope || payload.subjectId !== options.subjectId) {
96
+ throw new ForbiddenApiError("Checkout session capability is not scoped to this resource");
97
+ }
98
+ if (!payload.actions.includes(options.action)) {
99
+ throw new ForbiddenApiError("Checkout session capability cannot perform this action");
100
+ }
101
+ return payload;
102
+ }
103
+ export function extractPublicCapabilityToken(c, options = {}) {
104
+ const headerName = options.headerName ?? DEFAULT_CAPABILITY_HEADER;
105
+ const headerToken = c.req.header(headerName);
106
+ if (headerToken) {
107
+ return headerToken;
108
+ }
109
+ const cookieName = options.cookieName;
110
+ if (!cookieName) {
111
+ return null;
112
+ }
113
+ const cookieHeader = c.req.header("Cookie");
114
+ if (!cookieHeader) {
115
+ return null;
116
+ }
117
+ for (const part of cookieHeader.split(";")) {
118
+ const [rawName, ...rawValueParts] = part.trim().split("=");
119
+ if (rawName === cookieName) {
120
+ return decodeURIComponent(rawValueParts.join("="));
121
+ }
122
+ }
123
+ return null;
124
+ }
125
+ export function serializePublicCapabilityCookie(options) {
126
+ const secure = options.secure ?? true;
127
+ const sameSite = options.sameSite ?? "Lax";
128
+ const path = options.path ?? "/";
129
+ const parts = [
130
+ `${options.name}=${encodeURIComponent(options.token)}`,
131
+ "HttpOnly",
132
+ `SameSite=${sameSite}`,
133
+ `Path=${path}`,
134
+ `Expires=${options.expiresAt.toUTCString()}`,
135
+ ];
136
+ if (secure) {
137
+ parts.push("Secure");
138
+ }
139
+ return parts.join("; ");
140
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/hono",
3
- "version": "0.31.3",
3
+ "version": "0.32.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -94,18 +94,18 @@
94
94
  "drizzle-orm": "^0.45.2",
95
95
  "hono": "^4.12.10",
96
96
  "zod": "^4.3.6",
97
- "@voyantjs/core": "0.31.3",
98
- "@voyantjs/db": "0.31.3",
99
- "@voyantjs/types": "0.31.3",
100
- "@voyantjs/utils": "0.31.3",
101
- "@voyantjs/workflows": "0.31.3"
97
+ "@voyantjs/core": "0.32.0",
98
+ "@voyantjs/db": "0.32.0",
99
+ "@voyantjs/types": "0.32.0",
100
+ "@voyantjs/utils": "0.32.0",
101
+ "@voyantjs/workflows": "0.32.0"
102
102
  },
103
103
  "devDependencies": {
104
104
  "@cloudflare/workers-types": "^4.20260426.1",
105
105
  "typescript": "^6.0.2",
106
106
  "vitest": "^4.1.2",
107
107
  "@voyantjs/voyant-typescript-config": "0.1.0",
108
- "@voyantjs/workflows-orchestrator": "0.31.3"
108
+ "@voyantjs/workflows-orchestrator": "0.32.0"
109
109
  },
110
110
  "files": [
111
111
  "dist"