typespec-hono 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ import { type HttpOperation } from "@typespec/http";
2
+ import type { Program } from "@typespec/compiler";
3
+ import type { SecurityRequirement } from "./runtime.js";
4
+ /**
5
+ * What the DOCUMENT says a caller must satisfy, in the shape the document says it.
6
+ *
7
+ * ⚠️ **The scheme was being thrown away, and only "is a caller needed" survived.** `@useAuth(BearerAuth)`
8
+ * reaches OpenAPI as `security: [{ "BearerAuth": [] }]`, and this emitter reduced that to
9
+ * `deps.context(c, "required")`. A gate was emitted ONLY when the scheme carried scopes — so for
10
+ * bearer, api-key and basic, which is the common case, nothing carried which scheme at all. An
11
+ * application whose `context` read a cookie would happily serve a route the document says needs a
12
+ * bearer token, and nothing anywhere would notice.
13
+ *
14
+ * ⚠️ **Passed through, never enforced here.** Which credentials satisfy a scheme is the application's
15
+ * business and could not be anything else; which schemes an operation ACCEPTS is a contract fact and
16
+ * is now generated. That is the same split as `context` and `respond`, applied to the half that was
17
+ * missing.
18
+ */
19
+ export type { SecurityRequirement } from "./runtime.js";
20
+ /**
21
+ * The requirements an operation declares. Satisfying **any one** of them authorises the caller —
22
+ * which is what an array of `security` objects means in OpenAPI, and why this is a list of lists
23
+ * rather than a flat set of scopes.
24
+ *
25
+ * Empty when the operation declares `@useAuth(NoAuth)` or no authentication at all.
26
+ */
27
+ export declare function securityFor(program: Program, operation: HttpOperation): SecurityRequirement[];
28
+ /** The requirements as a TypeScript literal, for the generated call site. */
29
+ export declare function renderSecurity(requirements: readonly SecurityRequirement[]): string;
@@ -0,0 +1,48 @@
1
+ import { getAuthenticationForOperation } from "@typespec/http";
2
+ /**
3
+ * The requirements an operation declares. Satisfying **any one** of them authorises the caller —
4
+ * which is what an array of `security` objects means in OpenAPI, and why this is a list of lists
5
+ * rather than a flat set of scopes.
6
+ *
7
+ * Empty when the operation declares `@useAuth(NoAuth)` or no authentication at all.
8
+ */
9
+ export function securityFor(program, operation) {
10
+ const authentication = getAuthenticationForOperation(program, operation.operation);
11
+ const requirements = [];
12
+ for (const option of authentication?.options ?? []) {
13
+ const requirement = {};
14
+ let anonymous = false;
15
+ for (const scheme of option.schemes) {
16
+ /**
17
+ * ⚠️ **`NoAuth` inside an option means that option needs nothing**, which is how a spec says
18
+ * "authentication is optional here". It is not a scheme to demand, and emitting it as one
19
+ * would refuse every anonymous caller the document permits.
20
+ */
21
+ if (scheme.type === "noAuth") {
22
+ anonymous = true;
23
+ continue;
24
+ }
25
+ /**
26
+ * Scopes belong to the flows of an OAuth2 scheme; every other kind has none. Read from the
27
+ * scheme rather than assumed, and de-duplicated because two flows may name the same scope.
28
+ */
29
+ const scopes = scheme.type === "oauth2"
30
+ ? [...new Set(scheme.flows.flatMap((flow) => flow.scopes.map((scope) => scope.value)))]
31
+ : [];
32
+ requirement[scheme.id] = scopes;
33
+ }
34
+ if (anonymous && Object.keys(requirement).length === 0)
35
+ continue;
36
+ if (Object.keys(requirement).length > 0)
37
+ requirements.push(requirement);
38
+ }
39
+ return requirements;
40
+ }
41
+ /** The requirements as a TypeScript literal, for the generated call site. */
42
+ export function renderSecurity(requirements) {
43
+ return `[${requirements
44
+ .map((requirement) => `{ ${Object.entries(requirement)
45
+ .map(([scheme, scopes]) => `${JSON.stringify(scheme)}: [${scopes.map((s) => JSON.stringify(s)).join(", ")}]`)
46
+ .join(", ")} }`)
47
+ .join(", ")}]`;
48
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * What TypeSpec's compiler loads for this library.
3
+ *
4
+ * ⚠️ **There is deliberately no `$decorators` export**, here or in `typespec-http-zod`. Four once
5
+ * existed and every one let a spec state something `@typespec/openapi3` could not publish, so the
6
+ * emitted validator enforced a rule no caller reading the contract could see. A decorator is not a
7
+ * convenience; it is a second contract.
8
+ */
9
+ export { $lib } from "./lib.js";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * What TypeSpec's compiler loads for this library.
3
+ *
4
+ * ⚠️ **There is deliberately no `$decorators` export**, here or in `typespec-http-zod`. Four once
5
+ * existed and every one let a spec state something `@typespec/openapi3` could not publish, so the
6
+ * emitted validator enforced a rule no caller reading the contract could see. A decorator is not a
7
+ * convenience; it is a second contract.
8
+ */
9
+ export { $lib } from "./lib.js";
package/lib/main.tsp ADDED
@@ -0,0 +1,3 @@
1
+ // The emitter library itself — one diagnostic and its options schema. There are no decorators; see
2
+ // `src/tsp-index.ts` for why, and `typespec-http-zod` for the rest of the options this accepts.
3
+ import "../dist/src/tsp-index.js";
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "typespec-hono",
3
+ "version": "0.1.0",
4
+ "description": "TypeSpec emitter: generate a Hono server, and the Zod validators it enforces, from an HTTP service definition — agreeing with the OpenAPI document @typespec/openapi3 publishes from the same source.",
5
+ "keywords": [
6
+ "cloudflare-workers",
7
+ "codegen",
8
+ "hono",
9
+ "openapi",
10
+ "typespec",
11
+ "typespec-emitter",
12
+ "zod"
13
+ ],
14
+ "homepage": "https://github.com/bison-digital/typespec-hono#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/bison-digital/typespec-hono/issues"
17
+ },
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/bison-digital/typespec-hono.git"
22
+ },
23
+ "files": [
24
+ "lib/*.tsp",
25
+ "dist/**",
26
+ "!dist/test/**"
27
+ ],
28
+ "type": "module",
29
+ "main": "dist/src/index.js",
30
+ "exports": {
31
+ ".": {
32
+ "typespec": "./lib/main.tsp",
33
+ "types": "./dist/src/index.d.ts",
34
+ "default": "./dist/src/index.js"
35
+ },
36
+ "./runtime": {
37
+ "types": "./dist/src/runtime.d.ts",
38
+ "default": "./dist/src/runtime.js"
39
+ }
40
+ },
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "provenance": true
44
+ },
45
+ "dependencies": {
46
+ "typespec-http-zod": "^0.1.0"
47
+ },
48
+ "devDependencies": {
49
+ "@hono/zod-openapi": "^1.4.0",
50
+ "@hono/zod-validator": "^0.8.0",
51
+ "@types/node": "^26.0.0",
52
+ "@typespec/compiler": "1.15.0",
53
+ "@typespec/events": "0.85.0",
54
+ "@typespec/http": "1.15.0",
55
+ "@typespec/http-specs": "0.1.0-alpha.41",
56
+ "@typespec/openapi": "1.15.0",
57
+ "@typespec/openapi3": "1.15.0",
58
+ "@typespec/rest": "0.85.0",
59
+ "@typespec/sse": "0.85.0",
60
+ "@typespec/streams": "0.85.0",
61
+ "@typespec/versioning": "0.85.0",
62
+ "@typespec/xml": "0.85.0",
63
+ "hono": "^4.12.26",
64
+ "oxfmt": "^0.63.0",
65
+ "oxlint": "^1.78.0",
66
+ "typescript": "~7.0.2",
67
+ "typespec-hono": "link:.",
68
+ "vitest": "^4.1.9",
69
+ "zod": "^4.4.3"
70
+ },
71
+ "peerDependencies": {
72
+ "@hono/zod-validator": "^0.8.0",
73
+ "@typespec/compiler": "^1.15.0",
74
+ "@typespec/http": "^1.15.0",
75
+ "hono": "^4.12.0",
76
+ "zod": "^4.0.0"
77
+ },
78
+ "engines": {
79
+ "node": ">=22.0.0"
80
+ },
81
+ "tspMain": "lib/main.tsp",
82
+ "scripts": {
83
+ "build": "tsc -p tsconfig.build.json",
84
+ "test": "tsc -p tsconfig.build.json && vitest run",
85
+ "typecheck": "tsc -p tsconfig.json",
86
+ "lint": "oxlint src/ test/",
87
+ "format": "oxfmt",
88
+ "format:check": "oxfmt --check"
89
+ }
90
+ }