@pikku/inspector 0.12.75 → 0.12.82

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.
Files changed (223) hide show
  1. package/CHANGELOG.md +357 -0
  2. package/dist/add/add-analytics.d.ts +9 -0
  3. package/dist/add/add-analytics.js +178 -0
  4. package/dist/add/add-auth.js +0 -54
  5. package/dist/add/add-feature-flag.d.ts +2 -0
  6. package/dist/add/add-feature-flag.js +162 -0
  7. package/dist/add/add-middleware.js +66 -42
  8. package/dist/add/add-wire-addon.js +7 -4
  9. package/dist/add/add-wire-remote-addon.js +1 -0
  10. package/dist/error-codes.d.ts +1 -0
  11. package/dist/error-codes.js +1 -0
  12. package/dist/index.d.ts +3 -1
  13. package/dist/index.js +3 -1
  14. package/dist/inspector.js +5 -0
  15. package/dist/surface/collect-surface.d.ts +58 -0
  16. package/dist/surface/collect-surface.js +613 -0
  17. package/dist/types.d.ts +36 -12
  18. package/dist/utils/filter-inspector-state.js +14 -0
  19. package/dist/utils/load-addon-functions-meta.js +24 -11
  20. package/dist/utils/post-process.js +15 -0
  21. package/dist/utils/read-module-specifiers.d.ts +8 -0
  22. package/dist/utils/read-module-specifiers.js +23 -0
  23. package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
  24. package/dist/utils/read-tsconfig-out-dir.js +12 -0
  25. package/dist/utils/resolve-deploy-target.d.ts +7 -0
  26. package/dist/utils/resolve-deploy-target.js +15 -7
  27. package/dist/utils/serialize-inspector-state.d.ts +6 -0
  28. package/dist/utils/serialize-inspector-state.js +10 -0
  29. package/dist/visit.js +4 -0
  30. package/package.json +16 -6
  31. package/run-tests.sh +21 -6
  32. package/src/add/add-addon-bans.ts +0 -85
  33. package/src/add/add-agent-scorer.test.ts +0 -197
  34. package/src/add/add-agent-scorer.ts +0 -83
  35. package/src/add/add-agent.test.ts +0 -87
  36. package/src/add/add-agent.ts +0 -638
  37. package/src/add/add-approval-description.ts +0 -76
  38. package/src/add/add-auth.test.ts +0 -791
  39. package/src/add/add-auth.ts +0 -424
  40. package/src/add/add-channel.ts +0 -778
  41. package/src/add/add-cli-renderers.test.ts +0 -74
  42. package/src/add/add-cli.ts +0 -1006
  43. package/src/add/add-credential.ts +0 -186
  44. package/src/add/add-feature.test.ts +0 -306
  45. package/src/add/add-feature.ts +0 -253
  46. package/src/add/add-file-extends-core-type.ts +0 -66
  47. package/src/add/add-file-with-config.ts +0 -45
  48. package/src/add/add-file-with-factory.test.ts +0 -134
  49. package/src/add/add-file-with-factory.ts +0 -288
  50. package/src/add/add-functions.test.ts +0 -743
  51. package/src/add/add-functions.ts +0 -1548
  52. package/src/add/add-gateway.ts +0 -143
  53. package/src/add/add-http-route-ref.test.ts +0 -149
  54. package/src/add/add-http-route.ts +0 -530
  55. package/src/add/add-http-routes.ts +0 -471
  56. package/src/add/add-keyed-wiring.ts +0 -180
  57. package/src/add/add-mcp-prompt.ts +0 -157
  58. package/src/add/add-mcp-resource.ts +0 -175
  59. package/src/add/add-middleware.test.ts +0 -196
  60. package/src/add/add-middleware.ts +0 -879
  61. package/src/add/add-permission-auth.test.ts +0 -59
  62. package/src/add/add-permission.ts +0 -311
  63. package/src/add/add-personas.test.ts +0 -406
  64. package/src/add/add-personas.ts +0 -287
  65. package/src/add/add-queue-worker.ts +0 -117
  66. package/src/add/add-rpc-invocations.test.ts +0 -287
  67. package/src/add/add-rpc-invocations.ts +0 -221
  68. package/src/add/add-scenario-step.test.ts +0 -509
  69. package/src/add/add-schedule.ts +0 -120
  70. package/src/add/add-scope.test.ts +0 -419
  71. package/src/add/add-scope.ts +0 -221
  72. package/src/add/add-secret.ts +0 -10
  73. package/src/add/add-subject-scenario-steps.test.ts +0 -161
  74. package/src/add/add-system-role.test.ts +0 -297
  75. package/src/add/add-system-role.ts +0 -162
  76. package/src/add/add-trigger.ts +0 -165
  77. package/src/add/add-variable.ts +0 -10
  78. package/src/add/add-wire-addon.test.ts +0 -185
  79. package/src/add/add-wire-addon.ts +0 -146
  80. package/src/add/add-wire-remote-addon.ts +0 -80
  81. package/src/add/add-workflow-edge-cases.test.ts +0 -201
  82. package/src/add/add-workflow-fanout.test.ts +0 -166
  83. package/src/add/add-workflow-graph-foreach.test.ts +0 -174
  84. package/src/add/add-workflow-graph-input.test.ts +0 -94
  85. package/src/add/add-workflow-graph-notes.test.ts +0 -74
  86. package/src/add/add-workflow-graph.ts +0 -644
  87. package/src/add/add-workflow-on-error.test.ts +0 -118
  88. package/src/add/add-workflow-return-switch.test.ts +0 -121
  89. package/src/add/add-workflow-silent-drops.test.ts +0 -261
  90. package/src/add/add-workflow-sleep-expression.test.ts +0 -96
  91. package/src/add/add-workflow.test.ts +0 -155
  92. package/src/add/add-workflow.ts +0 -482
  93. package/src/add/addon-bans.test.ts +0 -121
  94. package/src/add/addon-contracts.test.ts +0 -221
  95. package/src/add/allow-escape-hatches.test.ts +0 -144
  96. package/src/add/audit-meta.test.ts +0 -75
  97. package/src/add/classified-sink-check.test.ts +0 -347
  98. package/src/add/config-value-metadata.test.ts +0 -263
  99. package/src/add/dynamic-import-check.test.ts +0 -119
  100. package/src/add/expect-eventually-scenario-only.test.ts +0 -108
  101. package/src/add/function-body-span.test.ts +0 -96
  102. package/src/add/inline-wiring-function.test.ts +0 -104
  103. package/src/add/pii-check.test.ts +0 -258
  104. package/src/add/rpc-type-cast.test.ts +0 -123
  105. package/src/add/scenario-instrumentation.test.ts +0 -89
  106. package/src/add/scenario-instrumentation.ts +0 -42
  107. package/src/add/scenario-marker.test.ts +0 -89
  108. package/src/add/scenario-not-extractable.test.ts +0 -159
  109. package/src/add/scenario-services.test.ts +0 -103
  110. package/src/add/scenario-skip.test.ts +0 -83
  111. package/src/add/validate-workflow-graph-addons.test.ts +0 -102
  112. package/src/add/wire-name-literal.test.ts +0 -117
  113. package/src/error-codes-emitted.test.ts +0 -76
  114. package/src/error-codes.ts +0 -145
  115. package/src/index.ts +0 -66
  116. package/src/inspector.ts +0 -575
  117. package/src/schema-generator.ts +0 -1
  118. package/src/source-file-cache.ts +0 -73
  119. package/src/tsconfig-paths.test.ts +0 -90
  120. package/src/types-map.ts +0 -141
  121. package/src/types.ts +0 -775
  122. package/src/utils/annotate-http-route-auth.test.ts +0 -161
  123. package/src/utils/annotate-http-route-auth.ts +0 -53
  124. package/src/utils/check-classified-sinks.ts +0 -215
  125. package/src/utils/check-pii-output.ts +0 -120
  126. package/src/utils/compute-diagnostics.test.ts +0 -106
  127. package/src/utils/compute-required-schemas.ts +0 -48
  128. package/src/utils/contract-hashes.test.ts +0 -553
  129. package/src/utils/contract-hashes.ts +0 -387
  130. package/src/utils/custom-types-generator.test.ts +0 -124
  131. package/src/utils/custom-types-generator.ts +0 -150
  132. package/src/utils/detect-schema-vendor.test.ts +0 -123
  133. package/src/utils/detect-schema-vendor.ts +0 -120
  134. package/src/utils/does-type-extend-core-type.ts +0 -53
  135. package/src/utils/ensure-function-metadata.ts +0 -385
  136. package/src/utils/extract-function-name.ts +0 -612
  137. package/src/utils/extract-function-node.ts +0 -70
  138. package/src/utils/extract-node-value.test.ts +0 -117
  139. package/src/utils/extract-node-value.ts +0 -172
  140. package/src/utils/extract-secret-usage.test.ts +0 -81
  141. package/src/utils/extract-secret-usage.ts +0 -73
  142. package/src/utils/extract-services.ts +0 -70
  143. package/src/utils/extract-surface-usage.test.ts +0 -147
  144. package/src/utils/extract-surface-usage.ts +0 -121
  145. package/src/utils/filter-inspector-state.test.ts +0 -2232
  146. package/src/utils/filter-inspector-state.ts +0 -1298
  147. package/src/utils/filter-utils.test.ts +0 -843
  148. package/src/utils/filter-utils.ts +0 -161
  149. package/src/utils/find-root-dir.ts +0 -68
  150. package/src/utils/find-zod-transform.test.ts +0 -70
  151. package/src/utils/find-zod-transform.ts +0 -103
  152. package/src/utils/get-exported-variable-name.ts +0 -48
  153. package/src/utils/get-files-and-methods.ts +0 -157
  154. package/src/utils/get-property-value.test.ts +0 -141
  155. package/src/utils/get-property-value.ts +0 -331
  156. package/src/utils/hash.ts +0 -26
  157. package/src/utils/literal-properties.ts +0 -97
  158. package/src/utils/load-addon-functions-meta.test.ts +0 -157
  159. package/src/utils/load-addon-functions-meta.ts +0 -508
  160. package/src/utils/load-addon-scopes.test.ts +0 -138
  161. package/src/utils/middleware.test.ts +0 -204
  162. package/src/utils/middleware.ts +0 -303
  163. package/src/utils/nested-project-filter.test.ts +0 -53
  164. package/src/utils/nested-project-filter.ts +0 -28
  165. package/src/utils/permissions-shorthand.test.ts +0 -79
  166. package/src/utils/permissions.test.ts +0 -125
  167. package/src/utils/permissions.ts +0 -129
  168. package/src/utils/post-process.test.ts +0 -1094
  169. package/src/utils/post-process.ts +0 -1604
  170. package/src/utils/resolve-addon-package.ts +0 -106
  171. package/src/utils/resolve-core-type.test.ts +0 -142
  172. package/src/utils/resolve-core-type.ts +0 -35
  173. package/src/utils/resolve-deploy-target.test.ts +0 -135
  174. package/src/utils/resolve-deploy-target.ts +0 -65
  175. package/src/utils/resolve-function-meta.ts +0 -25
  176. package/src/utils/resolve-function-types.ts +0 -42
  177. package/src/utils/resolve-identifier.ts +0 -46
  178. package/src/utils/resolve-ref-contract.ts +0 -71
  179. package/src/utils/resolve-versions.test.ts +0 -499
  180. package/src/utils/resolve-versions.ts +0 -228
  181. package/src/utils/schema-cache.test.ts +0 -124
  182. package/src/utils/schema-generator.test.ts +0 -272
  183. package/src/utils/schema-generator.ts +0 -780
  184. package/src/utils/secret-alias-services.test.ts +0 -146
  185. package/src/utils/secret-alias-services.ts +0 -58
  186. package/src/utils/serialize-inspector-state.ts +0 -742
  187. package/src/utils/serialize-mcp-json.ts +0 -150
  188. package/src/utils/serialize-middleware-groups-meta.ts +0 -36
  189. package/src/utils/serialize-openapi-json.ts +0 -277
  190. package/src/utils/serialize-permissions-groups-meta.ts +0 -7
  191. package/src/utils/single-declaration.ts +0 -57
  192. package/src/utils/type-utils.ts +0 -145
  193. package/src/utils/validate-auth-sessionless.ts +0 -30
  194. package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
  195. package/src/utils/validate-exposed-functions-gated.ts +0 -103
  196. package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
  197. package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
  198. package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
  199. package/src/utils/workflow/derive-workflow-plan.ts +0 -117
  200. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
  201. package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
  202. package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
  203. package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
  204. package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
  205. package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
  206. package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
  207. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
  208. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
  209. package/src/utils/workflow/dsl/index.ts +0 -11
  210. package/src/utils/workflow/dsl/patterns.ts +0 -440
  211. package/src/utils/workflow/dsl/validation.ts +0 -203
  212. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
  213. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
  214. package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
  215. package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
  216. package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
  217. package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
  218. package/src/utils/workflow/graph/index.ts +0 -28
  219. package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
  220. package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
  221. package/src/visit.ts +0 -181
  222. package/tsconfig.json +0 -19
  223. package/tsconfig.tsbuildinfo +0 -1
@@ -1,424 +0,0 @@
1
- import * as ts from 'typescript'
2
- import type { FunctionServicesMeta } from '@pikku/core/function'
3
- import type { AddWiring } from '../types.js'
4
- import { ErrorCode } from '../error-codes.js'
5
- import { extractServicesFromFunction } from '../utils/extract-services.js'
6
-
7
- /**
8
- * The pikku function id of the single shared auth handler the CLI generates
9
- * (`export const authHandler = pikkuSessionlessFunc(...)` in auth.gen.ts). An
10
- * exported top-level const collapses the catch-all `/api/auth/**` route onto one
11
- * worker, and the export name becomes the function id. Shared with the CLI
12
- * codegen and the post-process service stamp so all three agree on the same id
13
- * without the inspector having to import `@pikku/better-auth`.
14
- */
15
- export const AUTH_HANDLER_FUNC_ID = 'authHandler'
16
-
17
- /** The default better-auth base path when `basePath` is not configured. */
18
- const DEFAULT_BASE_PATH = '/api/auth'
19
-
20
- /**
21
- * Find the first `betterAuth({...})` call anywhere inside the `pikkuBetterAuth`
22
- * factory body. Supports both `(s) => betterAuth({...})` and
23
- * `(s) => { ...; return betterAuth({...}) }`.
24
- */
25
- const findBetterAuthCall = (node: ts.Node): ts.CallExpression | undefined => {
26
- let found: ts.CallExpression | undefined
27
- const visit = (n: ts.Node) => {
28
- if (found) return
29
- if (
30
- ts.isCallExpression(n) &&
31
- ts.isIdentifier(n.expression) &&
32
- n.expression.text === 'betterAuth'
33
- ) {
34
- found = n
35
- return
36
- }
37
- ts.forEachChild(n, visit)
38
- }
39
- visit(node)
40
- return found
41
- }
42
-
43
- /** Read a string-literal property off an object literal, if present. */
44
- const readStringProp = (
45
- obj: ts.ObjectLiteralExpression,
46
- name: string
47
- ): string | undefined => {
48
- const prop = obj.properties.find(
49
- (p) =>
50
- ts.isPropertyAssignment(p) &&
51
- (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
52
- p.name.text === name
53
- ) as ts.PropertyAssignment | undefined
54
- if (prop && ts.isStringLiteral(prop.initializer)) return prop.initializer.text
55
- return undefined
56
- }
57
-
58
- /** Find an object-literal-valued property off an object literal, if present. */
59
- const readObjectProp = (
60
- obj: ts.ObjectLiteralExpression,
61
- name: string
62
- ): ts.ObjectLiteralExpression | undefined => {
63
- const prop = obj.properties.find(
64
- (p) =>
65
- ts.isPropertyAssignment(p) &&
66
- (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
67
- p.name.text === name
68
- ) as ts.PropertyAssignment | undefined
69
- if (prop && ts.isObjectLiteralExpression(prop.initializer))
70
- return prop.initializer
71
- return undefined
72
- }
73
-
74
- /** Find an array-literal-valued property off an object literal, if present. */
75
- const readArrayProp = (
76
- obj: ts.ObjectLiteralExpression,
77
- name: string
78
- ): ts.ArrayLiteralExpression | undefined => {
79
- const prop = obj.properties.find(
80
- (p) =>
81
- ts.isPropertyAssignment(p) &&
82
- (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
83
- p.name.text === name
84
- ) as ts.PropertyAssignment | undefined
85
- if (prop && ts.isArrayLiteralExpression(prop.initializer))
86
- return prop.initializer
87
- return undefined
88
- }
89
-
90
- /** better-auth's own plugin entrypoint. */
91
- const BETTER_AUTH_PLUGINS_MODULE = 'better-auth/plugins'
92
-
93
- /**
94
- * What the names used in a `plugins: [...]` array were bound to by the file's
95
- * imports of {@link BETTER_AUTH_PLUGINS_MODULE}, so a project's local helper
96
- * called `admin` is not mistaken for better-auth's.
97
- */
98
- type PluginBindings = {
99
- /** Local name → the name it is exported under by better-auth. */
100
- named: Map<string, string>
101
- /** Locals bound by `import * as x from 'better-auth/plugins'`. */
102
- namespaces: Set<string>
103
- }
104
-
105
- const readPluginBindings = (source: ts.SourceFile): PluginBindings => {
106
- const named = new Map<string, string>()
107
- const namespaces = new Set<string>()
108
- for (const statement of source.statements) {
109
- if (
110
- !ts.isImportDeclaration(statement) ||
111
- !ts.isStringLiteral(statement.moduleSpecifier) ||
112
- statement.moduleSpecifier.text !== BETTER_AUTH_PLUGINS_MODULE
113
- ) {
114
- continue
115
- }
116
- const bindings = statement.importClause?.namedBindings
117
- if (!bindings) continue
118
- if (ts.isNamespaceImport(bindings)) {
119
- namespaces.add(bindings.name.text)
120
- continue
121
- }
122
- for (const element of bindings.elements) {
123
- named.set(element.name.text, (element.propertyName ?? element.name).text)
124
- }
125
- }
126
- return { named, namespaces }
127
- }
128
-
129
- /**
130
- * Read a `plugins: [...]` entry. better-auth plugins are factory calls —
131
- * `bearer()`, `twoFactor({ ... })`, `pikkuBan()`, or `plugins.bearer()` through a
132
- * namespace import; non-call entries are ignored. `betterAuthExport` is the
133
- * name better-auth exports the plugin under, and is absent for a plugin that
134
- * came from somewhere else, which no policy here applies to.
135
- */
136
- const readPluginEntry = (
137
- el: ts.Expression,
138
- bindings: PluginBindings
139
- ): { id: string; betterAuthExport?: string } | undefined => {
140
- if (!ts.isCallExpression(el)) return undefined
141
- const callee = el.expression
142
- if (ts.isIdentifier(callee)) {
143
- return {
144
- id: callee.text,
145
- betterAuthExport: bindings.named.get(callee.text),
146
- }
147
- }
148
- if (
149
- ts.isPropertyAccessExpression(callee) &&
150
- ts.isIdentifier(callee.expression) &&
151
- bindings.namespaces.has(callee.expression.text)
152
- ) {
153
- return { id: callee.name.text, betterAuthExport: callee.name.text }
154
- }
155
- return undefined
156
- }
157
-
158
- /**
159
- * True when `node` sits inside a GLOBAL middleware registration — i.e. an actual
160
- * global registration, not a bare standalone call or a route-scoped one.
161
- *
162
- * `addGlobalMiddleware(...)` is always global. `addHTTPMiddleware` is global only
163
- * in its array form (`addHTTPMiddleware([...])`) or with the `'*'` wildcard
164
- * pattern; a specific route pattern (`addHTTPMiddleware('/api/admin/*', [...])`)
165
- * scopes the middleware to that route and must NOT count as a global stateless
166
- * registration (#754).
167
- */
168
- const isInsideGlobalMiddlewareRegistration = (node: ts.Node): boolean => {
169
- let parent: ts.Node | undefined = node.parent
170
- while (parent) {
171
- if (ts.isCallExpression(parent) && ts.isIdentifier(parent.expression)) {
172
- const fn = parent.expression.text
173
- if (fn === 'addGlobalMiddleware') return true
174
- if (fn === 'addHTTPMiddleware') {
175
- const first = parent.arguments[0]
176
- if (!first) return false
177
- // String first arg → route pattern (global only when '*'); otherwise
178
- // the array form, which is global.
179
- return ts.isStringLiteral(first) ? first.text === '*' : true
180
- }
181
- }
182
- parent = parent.parent
183
- }
184
- return false
185
- }
186
-
187
- /**
188
- * Refusal for better-auth's `admin()`, which pikku does not support.
189
- *
190
- * `admin()` authorizes its endpoints against a `user.role` column, so wiring it
191
- * means running a second authorization model alongside pikku's scopes and
192
- * keeping one projected onto the other — coarsely, since any single
193
- * `admin:users:*` scope has to project to `role='admin'` and thereby unlocks
194
- * every one of its endpoints underneath. Pikku dropped that projection: user
195
- * management is `@pikku/addon-admin`'s scoped RPCs, and the one capability
196
- * `admin()` had that nothing else did — refusing a session to a banned user —
197
- * is `pikkuBan()`.
198
- *
199
- * Thrown rather than warned because the failure it prevents is silent. An app
200
- * that drops `admin()` without wiring `pikkuBan()` keeps its ban columns and its
201
- * UI, and simply stops enforcing bans.
202
- */
203
- const UNSUPPORTED_ADMIN_PLUGIN = (sourceFile: string): string =>
204
- `better-auth's admin() plugin is not supported by pikku (${sourceFile}).
205
-
206
- It authorizes against a 'user.role' column, while pikku authorizes on scopes —
207
- wiring both means one has to be projected onto the other, and the projection is
208
- coarser than the scopes it stands in for.
209
-
210
- Use pikkuBan() instead:
211
-
212
- import { pikkuBan } from '@pikku/better-auth'
213
- betterAuth({ plugins: [pikkuBan()] })
214
-
215
- pikkuBan() keeps the part of admin() that pikku cannot supply from outside
216
- better-auth — the banned/banReason/banExpires columns and the session hook that
217
- refuses a banned user a session. Everything else admin() offered is already
218
- scoped RPCs in @pikku/addon-admin: list, create, ban, remove, revoke sessions
219
- and set password.`
220
-
221
- /**
222
- * Detects `pikkuBetterAuth((services) => betterAuth({...}))` calls.
223
- *
224
- * `pikkuBetterAuth` is pure: it wraps a factory that returns a configured better-auth
225
- * instance and has NO side effects. The user assigns it to an exported binding,
226
- * e.g.
227
- *
228
- * export const auth = pikkuBetterAuth(async (services) => betterAuth({ ... }))
229
- *
230
- * The pikku CLI discovers that single export and generates a catch-all
231
- * `auth.gen.ts` that wires `${basePath}/**` to one shared handler, registers the
232
- * better-auth session middleware, and emits a `defineSecret` for every configured
233
- * social provider — so the auth routes and secret requirements flow through
234
- * normal inspection into the deploy manifest.
235
- *
236
- * This add-wiring records the exported binding name, source file, basePath, the
237
- * `socialProviders` keys, whether email/password is enabled, and the services
238
- * the factory touches. Exactly one `pikkuBetterAuth` is allowed per codebase.
239
- */
240
- export const addAuth: AddWiring = (logger, node, _checker, state) => {
241
- if (!ts.isCallExpression(node)) return
242
-
243
- const expression = node.expression
244
-
245
- // A user-registered stateless session middleware (custom mapSession) means the
246
- // CLI must NOT auto-generate its own default-map one — the generated one runs
247
- // first and pre-empts the user's via the `if (session) next()` short-circuit
248
- // (pikkujs/pikku#754). Only a GLOBAL registration counts (inside
249
- // addHTTPMiddleware/addGlobalMiddleware) — a bare betterAuthStatelessSession()
250
- // call (e.g. a test harness) is not a registration. Ignore generated files so
251
- // the emitted middleware can't self-trigger the skip.
252
- if (
253
- ts.isIdentifier(expression) &&
254
- expression.text === 'betterAuthStatelessSession' &&
255
- !node.getSourceFile().fileName.endsWith('.gen.ts') &&
256
- isInsideGlobalMiddlewareRegistration(node)
257
- ) {
258
- state.auth.userStatelessSession = true
259
- }
260
-
261
- // Same rule for the stateful variant: a user-registered global
262
- // betterAuthSession(...) (custom mapSession/impersonation/apiKey) means the CLI
263
- // must NOT auto-generate its own default one — the generated one runs first and
264
- // pre-empts the user's via the `if (session) next()` short-circuit. Stateful
265
- // analogue of the betterAuthStatelessSession skip above.
266
- if (
267
- ts.isIdentifier(expression) &&
268
- expression.text === 'betterAuthSession' &&
269
- !node.getSourceFile().fileName.endsWith('.gen.ts') &&
270
- isInsideGlobalMiddlewareRegistration(node)
271
- ) {
272
- state.auth.hasUserSessionMiddleware = true
273
- }
274
-
275
- if (!ts.isIdentifier(expression) || expression.text !== 'pikkuBetterAuth')
276
- return
277
-
278
- const sourceFile = node.getSourceFile().fileName
279
-
280
- // Walk up to the `export const <name> = pikkuBetterAuth(...)` binding.
281
- const varDecl = node.parent
282
- if (!ts.isVariableDeclaration(varDecl) || !ts.isIdentifier(varDecl.name)) {
283
- logger.critical(
284
- ErrorCode.AUTH_NOT_EXPORTED,
285
- `pikkuBetterAuth(...) must be assigned to an exported const, e.g. \`export const auth = pikkuBetterAuth((services) => betterAuth({...}))\` in ${sourceFile}`
286
- )
287
- return
288
- }
289
- const exportName = varDecl.name.text
290
-
291
- // VariableDeclaration -> VariableDeclarationList -> VariableStatement
292
- const declList = varDecl.parent
293
- const varStatement = declList?.parent
294
- const isConst =
295
- ts.isVariableDeclarationList(declList) &&
296
- (declList.flags & ts.NodeFlags.Const) !== 0
297
- const isExported =
298
- varStatement &&
299
- ts.isVariableStatement(varStatement) &&
300
- varStatement.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
301
- if (!isExported || !isConst) {
302
- logger.critical(
303
- ErrorCode.AUTH_NOT_EXPORTED,
304
- `pikkuBetterAuth(...) must be assigned to an exported const so the CLI can import it. Use \`export const ${exportName} = pikkuBetterAuth(...)\` in ${sourceFile}`
305
- )
306
- return
307
- }
308
-
309
- if (state.auth.definition) {
310
- logger.critical(
311
- ErrorCode.DUPLICATE_AUTH_DEFINITION,
312
- `Only one pikkuBetterAuth(...) is allowed per codebase. Found a second in ${sourceFile} (first: ${state.auth.definition.sourceFile}).`
313
- )
314
- return
315
- }
316
-
317
- // The single argument must be the factory: (services) => betterAuth({...}).
318
- const factory = node.arguments[0]
319
- if (
320
- !factory ||
321
- (!ts.isArrowFunction(factory) && !ts.isFunctionExpression(factory))
322
- ) {
323
- logger.critical(
324
- ErrorCode.MISSING_NAME,
325
- `pikkuBetterAuth(...) must take a factory function returning betterAuth(...), e.g. \`pikkuBetterAuth((services) => betterAuth({...}))\` in ${sourceFile}`
326
- )
327
- return
328
- }
329
-
330
- state.auth.files.add(sourceFile)
331
-
332
- // Derive the services the factory touches from its destructured first param —
333
- // the same convention as every other pikku wiring. A non-destructured param
334
- // yields optimized:false (handler gets all singleton services), with the
335
- // standard diagnostic steering the user to destructure.
336
- const services: FunctionServicesMeta = extractServicesFromFunction(factory)
337
-
338
- // Find the inner betterAuth({...}) call to read providers/basePath/credentials.
339
- let basePath = DEFAULT_BASE_PATH
340
- let hasCredentials = false
341
- let cookieCache = false
342
- const betterAuthCall = findBetterAuthCall(factory)
343
- const config = betterAuthCall?.arguments[0]
344
-
345
- if (config && ts.isObjectLiteralExpression(config)) {
346
- basePath = readStringProp(config, 'basePath') ?? DEFAULT_BASE_PATH
347
-
348
- // Detect `session.cookieCache.enabled` → drives the stateless middleware split.
349
- const session = readObjectProp(config, 'session')
350
- if (session) {
351
- const cookieCacheBlock = readObjectProp(session, 'cookieCache')
352
- if (cookieCacheBlock) {
353
- const enabledProp = cookieCacheBlock.properties.find(
354
- (p) =>
355
- ts.isPropertyAssignment(p) &&
356
- (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
357
- p.name.text === 'enabled'
358
- ) as ts.PropertyAssignment | undefined
359
- cookieCache =
360
- !enabledProp ||
361
- enabledProp.initializer.kind !== ts.SyntaxKind.FalseKeyword
362
- }
363
- }
364
-
365
- const emailAndPassword = readObjectProp(config, 'emailAndPassword')
366
- if (emailAndPassword) {
367
- // `emailAndPassword: { enabled: true }` — treat a present block without an
368
- // explicit `enabled: false` as credentials being available.
369
- const enabledProp = emailAndPassword.properties.find(
370
- (p) =>
371
- ts.isPropertyAssignment(p) &&
372
- ts.isIdentifier(p.name) &&
373
- p.name.text === 'enabled'
374
- ) as ts.PropertyAssignment | undefined
375
- hasCredentials =
376
- !enabledProp ||
377
- enabledProp.initializer.kind !== ts.SyntaxKind.FalseKeyword
378
- }
379
-
380
- const socialProviders = readObjectProp(config, 'socialProviders')
381
- if (socialProviders) {
382
- for (const prop of socialProviders.properties) {
383
- const key =
384
- (ts.isPropertyAssignment(prop) ||
385
- ts.isShorthandPropertyAssignment(prop)) &&
386
- (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))
387
- ? prop.name.text
388
- : undefined
389
- if (key && !state.auth.providers.includes(key)) {
390
- state.auth.providers.push(key)
391
- }
392
- }
393
- }
394
-
395
- const plugins = readArrayProp(config, 'plugins')
396
- if (plugins) {
397
- const bindings = readPluginBindings(node.getSourceFile())
398
- for (const el of plugins.elements) {
399
- const entry = readPluginEntry(el, bindings)
400
- if (!entry) continue
401
- if (entry.betterAuthExport === 'admin') {
402
- throw new Error(UNSUPPORTED_ADMIN_PLUGIN(sourceFile))
403
- }
404
- if (!state.auth.plugins.includes(entry.id)) {
405
- state.auth.plugins.push(entry.id)
406
- }
407
- }
408
- }
409
- } else {
410
- logger.warn(
411
- `pikkuBetterAuth in ${sourceFile}: could not statically find a betterAuth({...}) call inside the factory — social provider secrets will not be auto-wired.`
412
- )
413
- }
414
-
415
- state.auth.definition = {
416
- exportName,
417
- sourceFile,
418
- basePath,
419
- hasCredentials,
420
- cookieCache,
421
- plugins: [...state.auth.plugins],
422
- services,
423
- }
424
- }