permdock 0.0.0 → 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/README.md +268 -1
- package/dist/a2a/index.d.ts +178 -0
- package/dist/a2a/index.js +271 -0
- package/dist/ai-sdk/index.d.ts +74 -0
- package/dist/ai-sdk/index.js +137 -0
- package/dist/approval-required-TzNB9oQA.js +6 -0
- package/dist/approvals/index.d.ts +3 -0
- package/dist/approvals/index.js +4 -0
- package/dist/approvals-DaxY-Bdg.js +218 -0
- package/dist/approvers-aH0PJkmf.js +20 -0
- package/dist/arazzo-CTeVuXr5.js +70 -0
- package/dist/arazzo-aoTkqYsJ.js +54 -0
- package/dist/authzen/index.d.ts +48 -0
- package/dist/authzen/index.js +336 -0
- package/dist/better-auth/index.d.ts +92 -0
- package/dist/better-auth/index.js +324 -0
- package/dist/budget-JqcRqyhK.d.ts +11 -0
- package/dist/budget-NbEtbJC_.js +11 -0
- package/dist/capability-B68OFnZx.js +171 -0
- package/dist/capability-BDG_nkxt.d.ts +99 -0
- package/dist/catalog/index.d.ts +12 -0
- package/dist/catalog/index.js +2 -0
- package/dist/catalog-B0E6bggc.js +48 -0
- package/dist/catalog-H-vFGwWK.js +55 -0
- package/dist/catalog-doc-BSo_Q_-6.js +298 -0
- package/dist/catalog-idsHc0vo.js +2 -0
- package/dist/catalog-path-CnI0XTFK.js +12 -0
- package/dist/claude-agent/index.d.ts +81 -0
- package/dist/claude-agent/index.js +83 -0
- package/dist/clerk/index.d.ts +56 -0
- package/dist/clerk/index.js +280 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +20 -0
- package/dist/cli/index.d.ts +46 -0
- package/dist/cli/index.js +5 -0
- package/dist/cloud/index.d.ts +91 -0
- package/dist/cloud/index.js +390 -0
- package/dist/cloud-BNQTrmrr.js +50 -0
- package/dist/cloud-Cuxr6Czw.js +117 -0
- package/dist/collect-B7_TIfmg.js +49 -0
- package/dist/collect-O-tOtbnk.js +350 -0
- package/dist/collect-fRPAQIWb.js +2 -0
- package/dist/commands-BMh_Etm2.js +93 -0
- package/dist/compact-y6vfu2_f.js +19 -0
- package/dist/compare-pfyVnBjE.js +12 -0
- package/dist/compile-CQbvyIBr.d.ts +30 -0
- package/dist/compile-D22iwvuV.js +558 -0
- package/dist/config-BPYmjTOp.js +26 -0
- package/dist/connection-JvqwHODC.js +194 -0
- package/dist/context-CKEAabd7.js +73 -0
- package/dist/context-gtAwAILx.js +6 -0
- package/dist/convex/index.d.ts +40 -0
- package/dist/convex/index.js +55 -0
- package/dist/create-BKF3dgFk.d.ts +162 -0
- package/dist/create-CSkhDU9T.js +222 -0
- package/dist/credential-B_rkUNlY.js +326 -0
- package/dist/credentials-BgRX3WEg.js +268 -0
- package/dist/describe-Bt1YD5uS.js +99 -0
- package/dist/describe-DernyItf.d.ts +37 -0
- package/dist/diff-BUzf5Poi.js +48 -0
- package/dist/diff-x5Il8LVa.js +418 -0
- package/dist/digest-B3kc_PgQ.d.ts +19 -0
- package/dist/digest-BI-6aa6d.js +27 -0
- package/dist/doctor-BGle8Gqi.js +1314 -0
- package/dist/doctor-Bqo6FwUa.js +53 -0
- package/dist/drizzle/index.d.ts +72 -0
- package/dist/drizzle/index.js +160 -0
- package/dist/duration-xK2jGFVc.js +25 -0
- package/dist/elysia/index.d.ts +71 -0
- package/dist/elysia/index.js +103 -0
- package/dist/errors-DSMnAVNa.js +43 -0
- package/dist/errors-DbZ1YOnb.d.ts +138 -0
- package/dist/errors-Iy71s7Oj.js +177 -0
- package/dist/errors-m3u9TmDu.js +27 -0
- package/dist/evaluations-DsAU9ppl.js +479 -0
- package/dist/eve/index.d.ts +95 -0
- package/dist/eve/index.js +181 -0
- package/dist/express/index.d.ts +46 -0
- package/dist/express/index.js +108 -0
- package/dist/fastify/index.d.ts +56 -0
- package/dist/fastify/index.js +141 -0
- package/dist/files-B_wAGYmt.js +121 -0
- package/dist/fixtures-Cvaw0gDG.js +64 -0
- package/dist/freeze-y1_jQL4v.js +31 -0
- package/dist/from-snapshot-BMGnhmgv.js +552 -0
- package/dist/generate-CH7ThJ14.js +173 -0
- package/dist/grantee-Bv04_Vzf.js +521 -0
- package/dist/headers-B5RRv3Xm.js +6 -0
- package/dist/headers-C8SHQccD.d.ts +6 -0
- package/dist/helpers-DnSOrhB9.js +682 -0
- package/dist/hono/index.d.ts +70 -0
- package/dist/hono/index.js +131 -0
- package/dist/hosted-BwKQ1BTA.js +265 -0
- package/dist/http-DPuDaChb.d.ts +13 -0
- package/dist/http-GgVApX1k.js +111 -0
- package/dist/index-CltcQ4Bf.d.ts +138 -0
- package/dist/index-LF_hS-yn.d.ts +177 -0
- package/dist/index-fG6NSXlc.d.ts +9 -0
- package/dist/index-xIxJLwOQ.d.ts +415 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +21 -0
- package/dist/jwt/index.d.ts +87 -0
- package/dist/jwt/index.js +571 -0
- package/dist/kernel-YOGCWNLf.js +211 -0
- package/dist/kysely/index.d.ts +77 -0
- package/dist/kysely/index.js +129 -0
- package/dist/load-BZ24CpZP.js +69 -0
- package/dist/load-QOOm1z70.js +2 -0
- package/dist/load-jose-BL6LH4bL.js +11 -0
- package/dist/lru-8q4WZjBV.js +28 -0
- package/dist/manifest-DDQbC6K0.d.ts +118 -0
- package/dist/map-DowSO96j.js +158 -0
- package/dist/map-claims-B_6I1Rn-.js +360 -0
- package/dist/map-error-RJ3ZsUyA.js +16 -0
- package/dist/markers-B0hYLvMD.js +60 -0
- package/dist/may-use-CnFEjVEp.js +37 -0
- package/dist/mcp/index.d.ts +127 -0
- package/dist/mcp/index.js +563 -0
- package/dist/nest/index.d.ts +80 -0
- package/dist/nest/index.js +270 -0
- package/dist/next/client.d.ts +24 -0
- package/dist/next/client.js +31 -0
- package/dist/next/index.d.ts +110 -0
- package/dist/next/index.js +195 -0
- package/dist/next/plugin.d.ts +14 -0
- package/dist/next/plugin.js +2 -0
- package/dist/node/index.d.ts +43 -0
- package/dist/node/index.js +66 -0
- package/dist/openai/index.d.ts +71 -0
- package/dist/openai/index.js +105 -0
- package/dist/openapi/index.d.ts +93 -0
- package/dist/openapi/index.js +3 -0
- package/dist/openapi-CwePaF7C.js +164 -0
- package/dist/openapi-DWD0Uc_4.js +250 -0
- package/dist/openapi-DcqM8nU7.js +306 -0
- package/dist/openapi-import-BWRUNNeW.js +5959 -0
- package/dist/openapi-schema-D8j_T9tO.js +6902 -0
- package/dist/orpc/index.d.ts +64 -0
- package/dist/orpc/index.js +188 -0
- package/dist/otel/index.d.ts +13 -0
- package/dist/otel/index.js +202 -0
- package/dist/package-root-zGkxO3oN.js +30 -0
- package/dist/parse-DPfIvFMU.js +135 -0
- package/dist/pdp/index.d.ts +22 -0
- package/dist/pdp/index.js +795 -0
- package/dist/peer-BOr_kJE1.js +31 -0
- package/dist/permdock-yoZ7EjxX.js +2956 -0
- package/dist/permissions-B4apnahQ.d.ts +185 -0
- package/dist/permissions-B8vCg_bI.d.ts +52 -0
- package/dist/permissions-CofRqesP.js +142 -0
- package/dist/permissions-DC7GROPA.js +600 -0
- package/dist/pg-4GJWDUxG.js +953 -0
- package/dist/pins-CqE1jZuF.js +12 -0
- package/dist/plugin-4gr7IxFJ.js +65 -0
- package/dist/policy-ANp36PnI.js +971 -0
- package/dist/policy-CInmNGkM.d.ts +1959 -0
- package/dist/policy-Cq2fB8Q5.d.ts +20 -0
- package/dist/prisma/index.d.ts +135 -0
- package/dist/prisma/index.js +430 -0
- package/dist/problem-Cgp9-KZA.js +226 -0
- package/dist/prompt-CXYhLfsS.js +32 -0
- package/dist/protected-B4K3BKoX.js +138 -0
- package/dist/protected-BuPAycJT.d.ts +26 -0
- package/dist/protected-wY7nXhY9.d.ts +23 -0
- package/dist/provider-_wx46HpB.js +48 -0
- package/dist/provider-wTAUda2v.d.ts +6 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +6 -0
- package/dist/react/provider-client.d.ts +2 -0
- package/dist/react/provider-client.js +3 -0
- package/dist/react-native/index.d.ts +42 -0
- package/dist/react-native/index.js +174 -0
- package/dist/refs-ChLSxbST.js +90 -0
- package/dist/relations-r4soIFZB.js +426 -0
- package/dist/revocations-Cy06JNx2.d.ts +21 -0
- package/dist/rls-BOFU3chG.js +233 -0
- package/dist/rls-CJVja9Ww.js +2119 -0
- package/dist/rls-graph-DxQ39PJj.js +371 -0
- package/dist/rls-helpers-BxRuif0l.js +1346 -0
- package/dist/rls-import-CXV6LBlS.js +942 -0
- package/dist/rls-migrate-DrFPbf8p.js +366 -0
- package/dist/row-check-BajTgQZ-.d.ts +43 -0
- package/dist/run-CJ6UvS3a.js +155 -0
- package/dist/runtime-DP9ttFh1.js +27 -0
- package/dist/saas-BRrZzJkM.js +1193 -0
- package/dist/schema-P8yoma__.js +296 -0
- package/dist/scim/index.d.ts +2 -0
- package/dist/scim/index.js +2 -0
- package/dist/scim-9NFa0XVZ.js +1445 -0
- package/dist/scopes-cKVp_WTw.js +212 -0
- package/dist/server/index.d.ts +123 -0
- package/dist/server/index.js +8 -0
- package/dist/sha256-BO0swRXL.js +150 -0
- package/dist/sink-Bd9PSyfq.js +141 -0
- package/dist/sink-Cfr9-7YC.d.ts +104 -0
- package/dist/skills-C_lpV4Jx.js +50 -0
- package/dist/skills-Dx3QLnMY.js +110 -0
- package/dist/snapshot-DE8dE_hz.js +1051 -0
- package/dist/solid/index.d.ts +63 -0
- package/dist/solid/index.js +197 -0
- package/dist/source-DoBBYJi2.js +6 -0
- package/dist/sources-DOpW5Ila.d.ts +347 -0
- package/dist/sources-DVHM3Mwq.js +284 -0
- package/dist/sql-Bll1d7y0.js +22 -0
- package/dist/src-CTNT-PBW.js +430 -0
- package/dist/ssf/index.d.ts +3 -0
- package/dist/ssf/index.js +622 -0
- package/dist/store-Bq90395i.d.ts +27 -0
- package/dist/store-C6hFW6ta.js +508 -0
- package/dist/stores-CkG2HrSg.js +140 -0
- package/dist/stores-CvOnu_2T.d.ts +41 -0
- package/dist/stream-BhMfZOwr.d.ts +12 -0
- package/dist/stream-Q_xlB6Ko.js +52 -0
- package/dist/style-CTm1AXDT.js +22 -0
- package/dist/subject-C6dLxqHU.js +24 -0
- package/dist/subject-settings-xt7V845g.js +82 -0
- package/dist/supabase/index.d.ts +67 -0
- package/dist/supabase/index.js +4 -0
- package/dist/supabase/middleware.d.ts +77 -0
- package/dist/supabase/middleware.js +68 -0
- package/dist/supabase-D8UlBA7Z.js +83 -0
- package/dist/supabase-DTxGtYdi.js +796 -0
- package/dist/supabase-hook-DW-xP4hU.js +2 -0
- package/dist/supabase-hook-D_OqGb_V.js +1528 -0
- package/dist/svelte/Protected.svelte +34 -0
- package/dist/svelte/index.d.ts +10 -0
- package/dist/svelte/index.js +53 -0
- package/dist/svelte/runtime.d.ts +6 -0
- package/dist/svelte/runtime.js +3 -0
- package/dist/svelte/source.d.ts +10 -0
- package/dist/svelte/source.js +8 -0
- package/dist/terminal/index.d.ts +171 -0
- package/dist/terminal/index.js +821 -0
- package/dist/testing/index.d.ts +656 -0
- package/dist/testing/index.js +3208 -0
- package/dist/testing/saas/index.d.ts +194 -0
- package/dist/testing/saas/index.js +3 -0
- package/dist/testing/saas/permissions.d.ts +2 -0
- package/dist/testing/saas/permissions.js +2 -0
- package/dist/text-diff-BdkkYSbi.js +15 -0
- package/dist/thenable-okc2xDsj.js +10 -0
- package/dist/timeout-D51niuQP.js +8 -0
- package/dist/trpc/index.d.ts +80 -0
- package/dist/trpc/index.js +205 -0
- package/dist/types-B8w5PFtS.d.ts +141 -0
- package/dist/types-B9dmiiab.d.ts +66 -0
- package/dist/types-BBK-exvJ.d.ts +177 -0
- package/dist/types-BvBk1wgn.d.ts +118 -0
- package/dist/types-CxquUByI.d.ts +9 -0
- package/dist/types-D6Z6-rKJ.d.ts +312 -0
- package/dist/types-DbWdEfcm.d.ts +87 -0
- package/dist/types-bva3i4Gf.d.ts +98 -0
- package/dist/types-i4BAOvdb.d.ts +97 -0
- package/dist/unplugin/index.d.ts +7 -0
- package/dist/unplugin/index.js +30 -0
- package/dist/usage-1ozMubLY.js +42 -0
- package/dist/usage-CUnK6Ou9.js +530 -0
- package/dist/usage-PyEor6GA.js +2 -0
- package/dist/validity-D0uRmJKJ.js +224 -0
- package/dist/verifier-B90agnhl.js +461 -0
- package/dist/version-CnSh33Fj.js +16 -0
- package/dist/vocabulary-Dk0fs-Jk.d.ts +56 -0
- package/dist/vue/index.d.ts +57 -0
- package/dist/vue/index.js +203 -0
- package/dist/webmcp/index.d.ts +75 -0
- package/dist/webmcp/index.js +313 -0
- package/package.json +423 -4
- package/rulesets/permdock.yaml +29 -0
- package/schemas/catalog-v1.json +507 -0
- package/schemas/openapi/oas-3.1.json +1298 -0
- package/schemas/openapi/oas-3.2.json +1516 -0
- package/schemas/openapi/overlay-1.1.json +79 -0
- package/schemas/supabase-claims-v1.json +139 -0
- package/schemas/supabase-manifest-v1.json +217 -0
- package/skills/permdock/SKILL.md +66 -0
- package/skills/permdock-agents/SKILL.md +77 -0
- package/skills/permdock-agents/references/runtimes.md +211 -0
- package/skills/permdock-approvals/SKILL.md +80 -0
- package/skills/permdock-approvals/references/stores-and-resume.md +83 -0
- package/skills/permdock-audit/SKILL.md +67 -0
- package/skills/permdock-credentials/SKILL.md +55 -0
- package/skills/permdock-credentials/references/tokens-keys-links.md +127 -0
- package/skills/permdock-data/SKILL.md +57 -0
- package/skills/permdock-data/references/queries-and-rls.md +54 -0
- package/skills/permdock-data/references/relationships.md +65 -0
- package/skills/permdock-tenancy/SKILL.md +77 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +83 -0
- package/skills/permdock-wire/SKILL.md +116 -0
- package/skills/permdock-wire/references/adapters.md +480 -0
- package/skills/permdock-wire/references/policy-options.md +43 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { $ as DecisionSink, C as Policy, E as PolicyVocabulary, Zt as PermDock, at as LimitStore, ct as MembershipSource, ft as RelationSource, mr as Principal, pt as RoleSource, sn as PolicySource, tt as EntitlementSource, xt as SnapshotSource } from "../policy-CInmNGkM.js";
|
|
2
|
+
import { d as Permission } from "../permissions-B4apnahQ.js";
|
|
3
|
+
import { f as ApprovalStore } from "../types-B8w5PFtS.js";
|
|
4
|
+
import { n as RevocationFeed } from "../revocations-Cy06JNx2.js";
|
|
5
|
+
import { o as OtelWrap } from "../types-B9dmiiab.js";
|
|
6
|
+
import { r as PdpFactory } from "../types-BvBk1wgn.js";
|
|
7
|
+
import { S as ConnectionOptions, b as Connection, g as discoverViaSignatureAgent, h as WebBotAuthVerifier, n as OpenApiHooks, o as TenantOption, r as ProtectOptions, u as InvalidSignatureError, y as verifyWebBotAuth } from "../create-BKF3dgFk.js";
|
|
8
|
+
import { Context, Hono, MiddlewareHandler } from "hono";
|
|
9
|
+
import { SSEMessage, SSEStreamingApi } from "hono/streaming";
|
|
10
|
+
import { WSEvents } from "hono/ws";
|
|
11
|
+
//#region src/hono/create.d.ts
|
|
12
|
+
type HonoPermDockOptions<TUser = unknown> = {
|
|
13
|
+
readonly subject: (c: Context) => TUser | Promise<TUser>;
|
|
14
|
+
readonly tenant?: TenantOption<Context>;
|
|
15
|
+
readonly memberships?: MembershipSource | readonly MembershipSource[];
|
|
16
|
+
/** The object graph for relation grants that walk a parent chain; without it they deny. */
|
|
17
|
+
readonly relations?: RelationSource;
|
|
18
|
+
readonly entitlements?: EntitlementSource;
|
|
19
|
+
readonly customRoles?: RoleSource;
|
|
20
|
+
/** Hosted grants, read once per instance; see `PolicySource`. */
|
|
21
|
+
readonly policies?: PolicySource;
|
|
22
|
+
readonly store?: ApprovalStore;
|
|
23
|
+
readonly sink?: DecisionSink;
|
|
24
|
+
readonly limits?: LimitStore;
|
|
25
|
+
/** `createPermDock` from `permdock/pdp`; `protect` then decides delegated permissions remotely. */
|
|
26
|
+
readonly pdp?: PdpFactory;
|
|
27
|
+
/** Accepted for adapter parity; not read by this adapter. */
|
|
28
|
+
readonly snapshots?: SnapshotSource;
|
|
29
|
+
/** `(permdock) => withOtel(permdock, options)` from `permdock/otel`. */
|
|
30
|
+
readonly otel?: OtelWrap;
|
|
31
|
+
/** `(request) => verifyWebBotAuth(request, options)`; a verified bot becomes the actor. */
|
|
32
|
+
readonly webBotAuth?: WebBotAuthVerifier;
|
|
33
|
+
/** Ends or revalidates open sockets and streams. */
|
|
34
|
+
readonly revocations?: RevocationFeed;
|
|
35
|
+
};
|
|
36
|
+
/** The `Variables` both middlewares set; Hono merges them into the route's Env. */
|
|
37
|
+
type PermDockEnv<TData = unknown, V extends PolicyVocabulary = PolicyVocabulary> = {
|
|
38
|
+
readonly Variables: {
|
|
39
|
+
readonly permdock: PermDock<V>;
|
|
40
|
+
readonly permdockData: TData;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
type HonoPermDock<V extends PolicyVocabulary = PolicyVocabulary> = {
|
|
44
|
+
readonly permdock: () => MiddlewareHandler<PermDockEnv<unknown, V>>;
|
|
45
|
+
readonly protect: <TData = unknown>(permission: Permission, loadData?: (c: Context) => TData | Promise<TData>, protectOptions?: ProtectOptions) => MiddlewareHandler<PermDockEnv<NonNullable<TData>, V>>;
|
|
46
|
+
/** A long-lived connection for the request behind `c`. */
|
|
47
|
+
readonly connection: (c: Context, connectionOptions?: ConnectionOptions) => Promise<Connection>;
|
|
48
|
+
/** `upgradeWebSocket` events that close with `1008` when the connection aborts. */
|
|
49
|
+
readonly socket: <T = unknown>(connection: Connection, events: WSEvents<T>) => WSEvents<T>;
|
|
50
|
+
/**
|
|
51
|
+
* Writes `source` to a `streamSSE` stream, dropping items the subscriber
|
|
52
|
+
* cannot read, and resolves when it ends: after an `event: permdock` frame
|
|
53
|
+
* when the connection aborts, when the client disconnects, or when the
|
|
54
|
+
* source is done. Await it as the last statement of the callback.
|
|
55
|
+
*/
|
|
56
|
+
readonly sse: <T>(connection: Connection, stream: SSEStreamingApi, source: AsyncIterable<T>, options?: SseOptions<T>) => Promise<void>;
|
|
57
|
+
readonly permdockHandler: () => Hono;
|
|
58
|
+
readonly openapi: OpenApiHooks;
|
|
59
|
+
};
|
|
60
|
+
type SseOptions<T> = {
|
|
61
|
+
/** Items the subscriber cannot read under this permission are dropped. */
|
|
62
|
+
readonly items?: Permission<string, unknown, "instance">;
|
|
63
|
+
/** The row to check for an item, when the item wraps it. */
|
|
64
|
+
readonly unwrap?: (item: T) => unknown;
|
|
65
|
+
/** The frame for an item; defaults to `{ data: JSON.stringify(item) }`. */
|
|
66
|
+
readonly format?: (item: T) => SSEMessage;
|
|
67
|
+
};
|
|
68
|
+
export declare function createPermDock<TUser, TPrincipal extends Principal = Principal, V extends PolicyVocabulary = PolicyVocabulary>(policy: Policy<TUser, TPrincipal, V>, options: HonoPermDockOptions<TUser>): HonoPermDock<V>;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { type HonoPermDock, type HonoPermDockOptions, InvalidSignatureError, type PermDockEnv, type SseOptions, discoverViaSignatureAgent, verifyWebBotAuth };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { t as compact } from "../compact-y6vfu2_f.js";
|
|
2
|
+
import { r as PermDockRevokedError } from "../errors-Iy71s7Oj.js";
|
|
3
|
+
import { i as discoverViaSignatureAgent, o as invalidSignatureResponse, r as InvalidSignatureError, s as verifyWebBotAuth } from "../evaluations-DsAU9ppl.js";
|
|
4
|
+
import { r as tenantScope, t as createKernel } from "../create-CSkhDU9T.js";
|
|
5
|
+
import { t as problemFromError } from "../map-error-RJ3ZsUyA.js";
|
|
6
|
+
import { i as onRevoked, n as guardIterable, t as POLICY_VIOLATION } from "../stream-Q_xlB6Ko.js";
|
|
7
|
+
import { Hono } from "hono";
|
|
8
|
+
//#region src/hono/create.ts
|
|
9
|
+
function socket(connection, events) {
|
|
10
|
+
return {
|
|
11
|
+
...events,
|
|
12
|
+
onOpen: (evt, ws) => {
|
|
13
|
+
onRevoked(connection, (problem) => {
|
|
14
|
+
ws.close(POLICY_VIOLATION, problem.type);
|
|
15
|
+
});
|
|
16
|
+
if (!connection.signal.aborted) events.onOpen?.(evt, ws);
|
|
17
|
+
},
|
|
18
|
+
onClose: (evt, ws) => {
|
|
19
|
+
connection.close();
|
|
20
|
+
events.onClose?.(evt, ws);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function sse(connection, stream, source, options = {}) {
|
|
25
|
+
const disconnected = new Promise((resolve) => {
|
|
26
|
+
stream.onAbort(() => {
|
|
27
|
+
connection.close();
|
|
28
|
+
resolve({
|
|
29
|
+
done: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
const iterator = guardIterable(source, connection, compact({
|
|
35
|
+
items: options.items,
|
|
36
|
+
unwrap: options.unwrap,
|
|
37
|
+
onRevoked: (error) => error
|
|
38
|
+
}))[Symbol.asyncIterator]();
|
|
39
|
+
try {
|
|
40
|
+
for (;;) {
|
|
41
|
+
const next = await Promise.race([iterator.next(), disconnected]);
|
|
42
|
+
if (next.done === true || stream.aborted) return;
|
|
43
|
+
const item = next.value;
|
|
44
|
+
await stream.writeSSE(options.format?.(item) ?? { data: JSON.stringify(item) });
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (!(error instanceof PermDockRevokedError)) throw error;
|
|
48
|
+
if (!stream.aborted) await stream.writeSSE({
|
|
49
|
+
event: "permdock",
|
|
50
|
+
data: JSON.stringify(error.toProblemDetails())
|
|
51
|
+
});
|
|
52
|
+
} finally {
|
|
53
|
+
Promise.resolve(iterator.return(void 0)).catch(() => void 0);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function mapDownstream(c) {
|
|
57
|
+
const mapped = c.error === void 0 ? void 0 : problemFromError(c.error);
|
|
58
|
+
if (mapped !== void 0) {
|
|
59
|
+
c.res = void 0;
|
|
60
|
+
c.res = mapped;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function createPermDock(policy, options) {
|
|
64
|
+
const contexts = /* @__PURE__ */ new WeakMap();
|
|
65
|
+
const kernel = createKernel(policy, compact({
|
|
66
|
+
subject: (request) => {
|
|
67
|
+
const c = contexts.get(request);
|
|
68
|
+
return c === void 0 ? null : options.subject(c);
|
|
69
|
+
},
|
|
70
|
+
memberships: options.memberships,
|
|
71
|
+
relations: options.relations,
|
|
72
|
+
entitlements: options.entitlements,
|
|
73
|
+
customRoles: options.customRoles,
|
|
74
|
+
policies: options.policies,
|
|
75
|
+
store: options.store,
|
|
76
|
+
sink: options.sink,
|
|
77
|
+
limits: options.limits,
|
|
78
|
+
pdp: options.pdp,
|
|
79
|
+
webBotAuth: options.webBotAuth,
|
|
80
|
+
revocations: options.revocations,
|
|
81
|
+
adapter: "hono",
|
|
82
|
+
wrap: options.otel
|
|
83
|
+
}));
|
|
84
|
+
const bind = (c) => {
|
|
85
|
+
const raw = c.req.raw;
|
|
86
|
+
if (!contexts.has(raw)) contexts.set(raw, c);
|
|
87
|
+
return raw;
|
|
88
|
+
};
|
|
89
|
+
const scopeOf = (c) => tenantScope(options.tenant, c);
|
|
90
|
+
const permdock = () => async (c, next) => {
|
|
91
|
+
try {
|
|
92
|
+
c.set("permdock", await kernel.permdock(bind(c), await scopeOf(c)));
|
|
93
|
+
} catch (error) {
|
|
94
|
+
const response = invalidSignatureResponse(error);
|
|
95
|
+
if (response !== void 0) return response;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
await next();
|
|
99
|
+
mapDownstream(c);
|
|
100
|
+
};
|
|
101
|
+
const protect = (permission, loadData, protectOptions) => async (c, next) => {
|
|
102
|
+
const guard = await kernel.protect(permission, loadData === void 0 ? void 0 : () => loadData(c), protectOptions)(bind(c), await scopeOf(c));
|
|
103
|
+
if (!guard.ok) return guard.response;
|
|
104
|
+
c.set("permdock", guard.permdock);
|
|
105
|
+
c.set("permdockData", guard.data);
|
|
106
|
+
await next();
|
|
107
|
+
mapDownstream(c);
|
|
108
|
+
};
|
|
109
|
+
const connection = async (c, connectionOptions) => kernel.connection(bind(c), connectionOptions, await scopeOf(c));
|
|
110
|
+
const permdockHandler = () => {
|
|
111
|
+
const { POST, GET } = kernel.permdockHandler((request) => {
|
|
112
|
+
const c = contexts.get(request);
|
|
113
|
+
return c === void 0 ? { tenant: void 0 } : scopeOf(c);
|
|
114
|
+
});
|
|
115
|
+
const app = new Hono();
|
|
116
|
+
app.post("/", (c) => POST(bind(c)));
|
|
117
|
+
app.get("/", (c) => GET(bind(c)));
|
|
118
|
+
return app;
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
permdock,
|
|
122
|
+
protect,
|
|
123
|
+
connection,
|
|
124
|
+
socket,
|
|
125
|
+
sse,
|
|
126
|
+
permdockHandler,
|
|
127
|
+
openapi: kernel.openapi
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
export { InvalidSignatureError, createPermDock, discoverViaSignatureAgent, verifyWebBotAuth };
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { t as compact } from "./compact-y6vfu2_f.js";
|
|
2
|
+
import { n as freezeDeep } from "./freeze-y1_jQL4v.js";
|
|
3
|
+
import { E as splitPath, S as isForbiddenKey, c as getResource, h as isSelfParented, i as findPermission } from "./permissions-DC7GROPA.js";
|
|
4
|
+
import { c as indexPolicy, d as role, h as normalizeWhere, i as declaredRoleNames, o as deny, r as completeGrant, t as allow } from "./policy-ANp36PnI.js";
|
|
5
|
+
import { C as listPlans, b as findRole, s as flattenGrantee } from "./grantee-Bv04_Vzf.js";
|
|
6
|
+
import { t as parseDuration } from "./duration-xK2jGFVc.js";
|
|
7
|
+
import { n as sha256, t as bytesToBase64Url } from "./sha256-BO0swRXL.js";
|
|
8
|
+
//#region src/core/hosted.ts
|
|
9
|
+
function memoryPolicySource(document = null) {
|
|
10
|
+
const current = document === null ? null : parsePolicyDocument(document);
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
current: () => current,
|
|
13
|
+
refresh: () => Promise.resolve()
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function rejectUnsafe(value) {
|
|
20
|
+
if (value === null || typeof value !== "object") return;
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
for (const item of value) rejectUnsafe(item);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
for (const key of Object.keys(value)) {
|
|
26
|
+
if (isForbiddenKey(key)) throw new Error(`PermDock: unsafe policy document key '${key}'`);
|
|
27
|
+
rejectUnsafe(value[key]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Validates the document envelope; per-grant checks happen at merge time. */
|
|
31
|
+
function parsePolicyDocument(json) {
|
|
32
|
+
const input = typeof json === "string" ? JSON.parse(json) : json;
|
|
33
|
+
if (!isRecord(input)) throw new TypeError("PermDock: policy document must be an object");
|
|
34
|
+
const copy = JSON.parse(JSON.stringify(input));
|
|
35
|
+
rejectUnsafe(copy);
|
|
36
|
+
if (copy["v"] !== 1) throw new Error(`PermDock: unsupported policy document version '${String(copy["v"])}'`);
|
|
37
|
+
if (typeof copy["id"] !== "string" || typeof copy["fingerprint"] !== "string" || typeof copy["catalog"] !== "string" || typeof copy["issuedAt"] !== "number" || !Array.isArray(copy["grants"])) throw new TypeError("PermDock: malformed policy document");
|
|
38
|
+
return freezeDeep(copy);
|
|
39
|
+
}
|
|
40
|
+
const COMPARISONS = /* @__PURE__ */ new Set([
|
|
41
|
+
"eq",
|
|
42
|
+
"ne",
|
|
43
|
+
"gt",
|
|
44
|
+
"gte",
|
|
45
|
+
"lt",
|
|
46
|
+
"lte",
|
|
47
|
+
"contains"
|
|
48
|
+
]);
|
|
49
|
+
function safePath(path) {
|
|
50
|
+
if (typeof path !== "string" || path.length === 0) return false;
|
|
51
|
+
return splitPath(path).every((segment) => !isForbiddenKey(segment));
|
|
52
|
+
}
|
|
53
|
+
function portableValue(value) {
|
|
54
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return true;
|
|
55
|
+
if (Array.isArray(value)) return value.every(portableValue);
|
|
56
|
+
if (!isRecord(value)) return false;
|
|
57
|
+
const keys = Object.keys(value);
|
|
58
|
+
if (keys.length !== 1) return false;
|
|
59
|
+
if (keys[0] === "ref") return safePath(value["ref"]);
|
|
60
|
+
return keys[0] === "date" && typeof value["date"] === "string";
|
|
61
|
+
}
|
|
62
|
+
/** True for the portable subset only: no `opaque`, no `sqlFunction`, no unknown op. */
|
|
63
|
+
function isPortableCondition(value) {
|
|
64
|
+
if (!isRecord(value) || typeof value["op"] !== "string") return false;
|
|
65
|
+
const op = value["op"];
|
|
66
|
+
if (COMPARISONS.has(op)) return safePath(value["field"]) && portableValue(value["value"]);
|
|
67
|
+
switch (op) {
|
|
68
|
+
case "in":
|
|
69
|
+
case "notIn": return safePath(value["field"]) && (Array.isArray(value["value"]) || isRecord(value["value"])) && portableValue(value["value"]);
|
|
70
|
+
case "isNull": return safePath(value["field"]) && typeof value["value"] === "boolean";
|
|
71
|
+
case "and":
|
|
72
|
+
case "or": return Array.isArray(value["conditions"]) && value["conditions"].every(isPortableCondition);
|
|
73
|
+
case "not": return isPortableCondition(value["condition"]);
|
|
74
|
+
case "memberOf": return (value["scope"] === "tenant" || value["scope"] === "team" || value["scope"] === "resource") && safePath(value["field"]) && Array.isArray(value["roles"]) && value["roles"].every((item) => typeof item === "string") && (value["resource"] === void 0 || typeof value["resource"] === "string") && (value["parents"] === void 0 || Array.isArray(value["parents"]) && value["parents"].every((parent) => safePath(parent) || isRecord(parent) && safePath(parent["field"]) && typeof parent["resource"] === "string"));
|
|
75
|
+
default: return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function approvalRank(approval) {
|
|
79
|
+
if (approval === void 0) return;
|
|
80
|
+
if (approval === "human") return {
|
|
81
|
+
distinct: true,
|
|
82
|
+
stale: false,
|
|
83
|
+
quorum: 1,
|
|
84
|
+
ttl: Number.POSITIVE_INFINITY,
|
|
85
|
+
escalates: false
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
by: approval.stages === void 0 ? JSON.stringify(approval.by) : JSON.stringify([approval.mode, approval.stages]),
|
|
89
|
+
distinct: approval.distinct !== false,
|
|
90
|
+
stale: approval.staleOn === "resource-change",
|
|
91
|
+
quorum: approval.quorum ?? 1,
|
|
92
|
+
ttl: parseDuration(approval.ttl) ?? Number.POSITIVE_INFINITY,
|
|
93
|
+
escalates: approval.escalation !== void 0
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** A hosted approval must meet every approval a code allow requires on the permission. */
|
|
97
|
+
function approvalAtLeast(hosted, code) {
|
|
98
|
+
const mine = approvalRank(hosted);
|
|
99
|
+
for (const required of code) {
|
|
100
|
+
const theirs = approvalRank(required);
|
|
101
|
+
if (theirs === void 0) continue;
|
|
102
|
+
if (mine === void 0) return false;
|
|
103
|
+
if (theirs.distinct && !mine.distinct) return false;
|
|
104
|
+
if (theirs.stale && !mine.stale) return false;
|
|
105
|
+
if (theirs.by !== void 0 && mine.by !== theirs.by) return false;
|
|
106
|
+
if (mine.quorum < theirs.quorum || mine.ttl > theirs.ttl) return false;
|
|
107
|
+
if (mine.escalates && !theirs.escalates) return false;
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
/** A grantee kind this build does not know is never declared, so the hosted grant is dropped. */
|
|
112
|
+
function undeclaredKind(_grantee) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
function declaredGrantee(policy, grantee, permissionResource) {
|
|
116
|
+
switch (grantee.kind) {
|
|
117
|
+
case "role": return declaredRoleNames(policy).has(grantee.role);
|
|
118
|
+
case "plan": return listPlans(policy.vocabulary.plans).some((leaf) => leaf.key === grantee.plan);
|
|
119
|
+
case "relation": return grantee.resource === permissionResource && getResource(policy.permissions, grantee.resource)?.relations[grantee.relation] !== void 0 && (grantee.through === void 0 || grantee.through === "parent" && isSelfParented(getResource(policy.permissions, grantee.resource))) && (grantee.depth === void 0 || grantee.through === "parent" && Number.isInteger(grantee.depth) && grantee.depth >= 0 && grantee.depth <= 32);
|
|
120
|
+
case "anyone":
|
|
121
|
+
case "authenticated":
|
|
122
|
+
case "actor":
|
|
123
|
+
case "assurance": return false;
|
|
124
|
+
default: return undeclaredKind(grantee);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** Approvers are declared roles, plans or relations, or any authenticated human. */
|
|
128
|
+
function approvalAcceptable(policy, approval, permissionResource) {
|
|
129
|
+
if (approval === void 0 || approval === "human") return true;
|
|
130
|
+
if (!isRecord(approval)) return false;
|
|
131
|
+
if (approval["mode"] !== void 0 || approval["stages"] !== void 0) return false;
|
|
132
|
+
if (approval["distinct"] !== void 0 && typeof approval["distinct"] !== "boolean") return false;
|
|
133
|
+
if (approval["staleOn"] !== void 0 && (approval["staleOn"] !== "resource-change" || getResource(policy.permissions, permissionResource)?.version === void 0)) return false;
|
|
134
|
+
if (approval["quorum"] !== void 0 && (!Number.isInteger(approval["quorum"]) || approval["quorum"] < 1)) return false;
|
|
135
|
+
if (approval["ttl"] !== void 0 && (typeof approval["ttl"] !== "string" || parseDuration(approval["ttl"]) === void 0)) return false;
|
|
136
|
+
const escalation = approval["escalation"];
|
|
137
|
+
if (escalation !== void 0 && (!isRecord(escalation) || typeof escalation["after"] !== "string" || parseDuration(escalation["after"]) === void 0 || !granteesAcceptable(policy, escalation["to"], permissionResource))) return false;
|
|
138
|
+
if (approval["by"] === void 0) return true;
|
|
139
|
+
return granteesAcceptable(policy, approval["by"], permissionResource);
|
|
140
|
+
}
|
|
141
|
+
function granteesAcceptable(policy, raw, permissionResource) {
|
|
142
|
+
if (raw === void 0) return false;
|
|
143
|
+
const items = flattenGrantee(raw);
|
|
144
|
+
return items.length > 0 && items.every((item) => isRecord(item) && item.kind !== "relation" && (item.kind === "authenticated" || declaredGrantee(policy, item, permissionResource)));
|
|
145
|
+
}
|
|
146
|
+
function buildGrant(policy, document, raw) {
|
|
147
|
+
if (!isRecord(raw) || typeof raw["id"] !== "string" || typeof raw["permission"] !== "string" || raw["effect"] !== void 0 && raw["effect"] !== "allow" && raw["effect"] !== "deny" || raw["to"] === void 0) return {
|
|
148
|
+
ok: false,
|
|
149
|
+
reason: "invalid"
|
|
150
|
+
};
|
|
151
|
+
const permission = findPermission(policy.permissions, raw["permission"]);
|
|
152
|
+
if (permission === void 0) return {
|
|
153
|
+
ok: false,
|
|
154
|
+
reason: "unknown-permission"
|
|
155
|
+
};
|
|
156
|
+
if (!policy.hostable.includes(permission.key)) return {
|
|
157
|
+
ok: false,
|
|
158
|
+
reason: "not-hostable"
|
|
159
|
+
};
|
|
160
|
+
const to = flattenGrantee(raw["to"]);
|
|
161
|
+
if (to.length === 0 || !to.every((item) => isRecord(item) && declaredGrantee(policy, item, permission.resource))) return {
|
|
162
|
+
ok: false,
|
|
163
|
+
reason: "unknown-grantee"
|
|
164
|
+
};
|
|
165
|
+
for (const condition of [raw["where"], raw["check"]]) if (condition !== void 0 && !isPortableCondition(condition)) return {
|
|
166
|
+
ok: false,
|
|
167
|
+
reason: "non-portable"
|
|
168
|
+
};
|
|
169
|
+
if (!approvalAcceptable(policy, raw["approval"], permission.resource)) return {
|
|
170
|
+
ok: false,
|
|
171
|
+
reason: "invalid"
|
|
172
|
+
};
|
|
173
|
+
if (raw["fields"] !== void 0 && !(Array.isArray(raw["fields"]) && raw["fields"].every((field) => typeof field === "string"))) return {
|
|
174
|
+
ok: false,
|
|
175
|
+
reason: "invalid"
|
|
176
|
+
};
|
|
177
|
+
const effect = raw["effect"] ?? "allow";
|
|
178
|
+
const options = compact({
|
|
179
|
+
where: raw["where"] === void 0 ? void 0 : normalizeWhere(raw["where"]),
|
|
180
|
+
check: raw["check"] === void 0 ? void 0 : normalizeWhere(raw["check"]),
|
|
181
|
+
approval: raw["approval"],
|
|
182
|
+
fields: raw["fields"]
|
|
183
|
+
});
|
|
184
|
+
let built;
|
|
185
|
+
try {
|
|
186
|
+
const roleItem = to.length === 1 && to[0]?.kind === "role" ? to[0] : null;
|
|
187
|
+
if (roleItem === null) {
|
|
188
|
+
const partial = (effect === "allow" ? allow : deny)(permission, {
|
|
189
|
+
...options,
|
|
190
|
+
to
|
|
191
|
+
});
|
|
192
|
+
built = completeGrant(partial);
|
|
193
|
+
} else {
|
|
194
|
+
const binding = policy.rolesByName.get(roleItem.role);
|
|
195
|
+
const leaf = findRole(policy.vocabulary.roles, roleItem.role);
|
|
196
|
+
const [first] = role(leaf ?? roleItem.role, [(effect === "allow" ? allow : deny)(permission, options)], binding?.on === void 0 ? {} : { on: binding.on }).grants;
|
|
197
|
+
if (first === void 0) throw new TypeError("PermDock: role produced no grant");
|
|
198
|
+
built = first;
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
reason: "invalid"
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (effect === "allow") {
|
|
207
|
+
const codeApprovals = policy.grants.filter((grant) => grant.effect === "allow" && grant.hosted === void 0 && grant.permission.key === permission.key).map((grant) => grant.approval);
|
|
208
|
+
if (!approvalAtLeast(built.approval, codeApprovals)) return {
|
|
209
|
+
ok: false,
|
|
210
|
+
reason: "weaker-approval"
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
ok: true,
|
|
215
|
+
grant: freezeDeep({
|
|
216
|
+
...built,
|
|
217
|
+
hosted: {
|
|
218
|
+
document: document.fingerprint,
|
|
219
|
+
grant: raw["id"]
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* The code policy plus the hosted grants that pass every rule. Hosted
|
|
226
|
+
* allows OR with code allows and any deny still wins, so a hosted grant can
|
|
227
|
+
* never override a code deny; one that fails a rule is dropped, not thrown.
|
|
228
|
+
*/
|
|
229
|
+
function mergeHostedGrants(policy, document) {
|
|
230
|
+
if (document === null || document.grants.length === 0) return {
|
|
231
|
+
policy,
|
|
232
|
+
dropped: []
|
|
233
|
+
};
|
|
234
|
+
const merged = [];
|
|
235
|
+
const dropped = [];
|
|
236
|
+
for (const raw of document.grants) {
|
|
237
|
+
const result = buildGrant(policy, document, raw);
|
|
238
|
+
if (result.ok) {
|
|
239
|
+
merged.push(result.grant);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
dropped.push(freezeDeep({
|
|
243
|
+
kind: "hosted-grant-dropped",
|
|
244
|
+
document: document.fingerprint,
|
|
245
|
+
grant: isRecord(raw) && typeof raw.id === "string" ? raw.id : "",
|
|
246
|
+
reason: result.reason
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
if (merged.length === 0) return {
|
|
250
|
+
policy,
|
|
251
|
+
dropped
|
|
252
|
+
};
|
|
253
|
+
const grants = [...policy.grants, ...merged];
|
|
254
|
+
return {
|
|
255
|
+
policy: freezeDeep({
|
|
256
|
+
...policy,
|
|
257
|
+
grants,
|
|
258
|
+
index: indexPolicy(policy.roles, grants, policy.vocabulary),
|
|
259
|
+
fingerprint: bytesToBase64Url(sha256(`${policy.fingerprint}:${document.fingerprint}`))
|
|
260
|
+
}),
|
|
261
|
+
dropped
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
export { parsePolicyDocument as i, memoryPolicySource as n, mergeHostedGrants as r, isPortableCondition as t };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
//#region src/node/http.d.ts
|
|
3
|
+
type NodeRequest = IncomingMessage & {
|
|
4
|
+
readonly originalUrl?: string;
|
|
5
|
+
readonly protocol?: string;
|
|
6
|
+
readonly body?: unknown;
|
|
7
|
+
};
|
|
8
|
+
declare function toRequest(req: NodeRequest, stream?: IncomingMessage | null): Request;
|
|
9
|
+
declare function sendResponse(res: ServerResponse, response: Response): Promise<void>;
|
|
10
|
+
declare const fromResponse: typeof sendResponse;
|
|
11
|
+
declare function isServerResponse(value: unknown): value is ServerResponse;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { toRequest as a, sendResponse as i, fromResponse as n, isServerResponse as r, NodeRequest as t };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
//#region src/node/http.ts
|
|
2
|
+
function toRequest(req, stream = req) {
|
|
3
|
+
const host = headerValue(req.headers.host) ?? "localhost";
|
|
4
|
+
const url = `${req.protocol ?? "http"}://${host}${req.originalUrl ?? req.url ?? "/"}`;
|
|
5
|
+
const headers = new Headers();
|
|
6
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
7
|
+
if (typeof value === "string") {
|
|
8
|
+
headers.set(key, value);
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(value)) for (const item of value) headers.append(key, item);
|
|
12
|
+
}
|
|
13
|
+
const method = req.method ?? "GET";
|
|
14
|
+
if (method === "GET" || method === "HEAD") return new Request(url, {
|
|
15
|
+
method,
|
|
16
|
+
headers
|
|
17
|
+
});
|
|
18
|
+
const parsed = bodyOf(req, headers);
|
|
19
|
+
if (parsed !== void 0) return new Request(url, {
|
|
20
|
+
method,
|
|
21
|
+
headers,
|
|
22
|
+
body: parsed
|
|
23
|
+
});
|
|
24
|
+
if (stream === null) return new Request(url, {
|
|
25
|
+
method,
|
|
26
|
+
headers
|
|
27
|
+
});
|
|
28
|
+
const init = {
|
|
29
|
+
method,
|
|
30
|
+
headers,
|
|
31
|
+
body: incomingBody(stream),
|
|
32
|
+
duplex: "half"
|
|
33
|
+
};
|
|
34
|
+
return new Request(url, init);
|
|
35
|
+
}
|
|
36
|
+
function chunkOf(chunk) {
|
|
37
|
+
return typeof chunk === "string" ? Buffer.from(chunk) : new Uint8Array(chunk);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Pull-based with `highWaterMark: 0`: nothing is read from `req` until the
|
|
41
|
+
* Web body is consumed, so a later body parser (multer, `express.json()`)
|
|
42
|
+
* still sees the whole stream.
|
|
43
|
+
*/
|
|
44
|
+
function incomingBody(req) {
|
|
45
|
+
return new ReadableStream({ pull(controller) {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const drain = () => {
|
|
48
|
+
const chunk = req.read();
|
|
49
|
+
if (chunk !== null) {
|
|
50
|
+
controller.enqueue(chunkOf(chunk));
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (req.readableEnded) {
|
|
54
|
+
controller.close();
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
if (drain()) {
|
|
60
|
+
resolve();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const cleanup = () => {
|
|
64
|
+
req.off("readable", onReadable);
|
|
65
|
+
req.off("end", onEnd);
|
|
66
|
+
req.off("error", onError);
|
|
67
|
+
};
|
|
68
|
+
const onReadable = () => {
|
|
69
|
+
if (drain()) {
|
|
70
|
+
cleanup();
|
|
71
|
+
resolve();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const onEnd = () => {
|
|
75
|
+
cleanup();
|
|
76
|
+
controller.close();
|
|
77
|
+
resolve();
|
|
78
|
+
};
|
|
79
|
+
const onError = (err) => {
|
|
80
|
+
cleanup();
|
|
81
|
+
controller.error(err);
|
|
82
|
+
resolve();
|
|
83
|
+
};
|
|
84
|
+
req.on("readable", onReadable);
|
|
85
|
+
req.once("end", onEnd);
|
|
86
|
+
req.once("error", onError);
|
|
87
|
+
});
|
|
88
|
+
} }, { highWaterMark: 0 });
|
|
89
|
+
}
|
|
90
|
+
function headerValue(value) {
|
|
91
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
92
|
+
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
93
|
+
}
|
|
94
|
+
function bodyOf(req, headers) {
|
|
95
|
+
if (req.body === void 0) return;
|
|
96
|
+
if (typeof req.body === "string") return req.body;
|
|
97
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
98
|
+
return JSON.stringify(req.body);
|
|
99
|
+
}
|
|
100
|
+
async function sendResponse(res, response) {
|
|
101
|
+
res.statusCode = response.status;
|
|
102
|
+
for (const [key, value] of response.headers.entries()) res.setHeader(key, value);
|
|
103
|
+
const body = Buffer.from(await response.arrayBuffer());
|
|
104
|
+
res.end(body);
|
|
105
|
+
}
|
|
106
|
+
const fromResponse = sendResponse;
|
|
107
|
+
function isServerResponse(value) {
|
|
108
|
+
return typeof value === "object" && value !== null && "setHeader" in value && typeof value.setHeader === "function" && "end" in value;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { toRequest as i, isServerResponse as n, sendResponse as r, fromResponse as t };
|