@pikku/inspector 0.12.106 → 0.12.108

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 (96) hide show
  1. package/dist/add/add-auth.d.ts +1 -9
  2. package/dist/add/add-auth.js +64 -17
  3. package/dist/add/add-cli.js +4 -0
  4. package/dist/add/add-functions.js +27 -31
  5. package/dist/add/add-keyed-wiring.js +22 -2
  6. package/dist/add/add-middleware.js +17 -16
  7. package/dist/add/add-permission.d.ts +0 -3
  8. package/dist/add/add-permission.js +213 -22
  9. package/dist/add/add-scope.d.ts +1 -7
  10. package/dist/add/add-scope.js +1 -21
  11. package/dist/add/check-indirect-registrations.d.ts +9 -0
  12. package/dist/add/check-indirect-registrations.js +114 -0
  13. package/dist/error-codes.d.ts +1 -0
  14. package/dist/error-codes.js +1 -0
  15. package/dist/finalize.d.ts +10 -0
  16. package/dist/finalize.js +102 -0
  17. package/dist/index.d.ts +8 -6
  18. package/dist/index.js +6 -5
  19. package/dist/initial-state.d.ts +7 -0
  20. package/dist/initial-state.js +216 -0
  21. package/dist/inspector.d.ts +6 -4
  22. package/dist/inspector.js +28 -302
  23. package/dist/lite.d.ts +26 -0
  24. package/dist/lite.js +25 -0
  25. package/dist/source-file-cache.d.ts +11 -0
  26. package/dist/source-file-cache.js +34 -10
  27. package/dist/types-map.js +2 -1
  28. package/dist/types.d.ts +32 -0
  29. package/dist/utils/apply-declared-scopes.d.ts +8 -0
  30. package/dist/utils/apply-declared-scopes.js +21 -0
  31. package/dist/utils/canonical-type-string.d.ts +14 -0
  32. package/dist/utils/canonical-type-string.js +162 -0
  33. package/dist/utils/context-based-id.d.ts +7 -0
  34. package/dist/utils/context-based-id.js +9 -0
  35. package/dist/utils/custom-types-generator.d.ts +15 -1
  36. package/dist/utils/custom-types-generator.js +17 -5
  37. package/dist/utils/ensure-function-metadata.d.ts +0 -14
  38. package/dist/utils/ensure-function-metadata.js +4 -1
  39. package/dist/utils/extract-function-name.d.ts +1 -1
  40. package/dist/utils/extract-function-name.js +1 -3
  41. package/dist/utils/filter-inspector-state.js +31 -1
  42. package/dist/utils/get-property-value.d.ts +5 -0
  43. package/dist/utils/get-property-value.js +14 -0
  44. package/dist/utils/inspector-constants.d.ts +16 -0
  45. package/dist/utils/inspector-constants.js +16 -0
  46. package/dist/utils/post-process.d.ts +1 -2
  47. package/dist/utils/post-process.js +27 -27
  48. package/dist/utils/read-jsx-literal-text.js +31 -1
  49. package/dist/utils/resolve-callee-name.d.ts +27 -0
  50. package/dist/utils/resolve-callee-name.js +147 -0
  51. package/dist/utils/resolve-service-types.d.ts +8 -0
  52. package/dist/utils/resolve-service-types.js +21 -0
  53. package/dist/utils/schema-generator.js +29 -2
  54. package/dist/utils/serialize-inspector-state.d.ts +7 -1
  55. package/dist/utils/serialize-inspector-state.js +16 -1
  56. package/dist/utils/workflow/dsl/patterns.d.ts +1 -8
  57. package/dist/utils/workflow/dsl/patterns.js +1 -7
  58. package/dist/visit.js +2 -0
  59. package/package.json +25 -9
  60. package/CHANGELOG.md +0 -4368
  61. package/OPTIMIZATION-PLAN.md +0 -201
  62. package/knowledge/decisions/a-channel-with-auth-false-marks-its-connect-and-disconnect-sessionless.md +0 -22
  63. package/knowledge/decisions/a-declared-scope-id-never-shadows-a-definition.md +0 -20
  64. package/knowledge/decisions/a-dsl-step-virtual-user-is-the-literal-name-in-its-options.md +0 -27
  65. package/knowledge/decisions/a-feature-flags-anyof-is-read-from-the-ast-and-an-empty-list-is-refused.md +0 -20
  66. package/knowledge/decisions/a-non-literal-string-array-reads-as-absent.md +0 -25
  67. package/knowledge/decisions/a-permission-with-an-underscore-data-parameter-takes-no-data.md +0 -20
  68. package/knowledge/decisions/a-provider-login-virtual-user-is-never-runnable.md +0 -27
  69. package/knowledge/decisions/a-remote-addon-bound-credential-or-secret-must-be-wired.md +0 -27
  70. package/knowledge/decisions/a-route-auth-overrides-its-groups-auth.md +0 -22
  71. package/knowledge/decisions/a-route-requires-a-session-when-any-auth-signal-says-so.md +0 -29
  72. package/knowledge/decisions/a-webhook-source-with-verify-gets-a-singleton-secret-credential.md +0 -21
  73. package/knowledge/decisions/a-wire-with-auth-disabled-cannot-point-at-a-pikkufunc.md +0 -21
  74. package/knowledge/decisions/addon-contracts-cannot-wire-or-declare-handlers.md +0 -25
  75. package/knowledge/decisions/addon-declarations-name-the-secrets-and-credentials-the-app-lends-it.md +0 -19
  76. package/knowledge/decisions/an-inline-wiring-function-is-sessionless-unless-built-with-pikkufunc.md +0 -23
  77. package/knowledge/decisions/better-auth-admin-plugin-is-refused.md +0 -24
  78. package/knowledge/decisions/define-screens-scopes-must-be-string-literals.md +0 -18
  79. package/knowledge/decisions/index.md +0 -38
  80. package/knowledge/decisions/inspector-records-a-non-literal-global-grant-as-source-text.md +0 -20
  81. package/knowledge/decisions/only-one-pikku-better-auth-export-per-codebase.md +0 -19
  82. package/knowledge/decisions/scope-references-and-secret-aliases-are-critical-diagnostics.md +0 -23
  83. package/knowledge/decisions/scope-segments-may-not-be-wildcards-or-contain-colons.md +0 -23
  84. package/knowledge/decisions/shorthand-permission-entries-are-extracted.md +0 -22
  85. package/knowledge/decisions/system-roles-declare-literal-unqualified-scopes.md +0 -28
  86. package/knowledge/decisions/the-inspector-records-agent-permissions-only-to-aggregate-services.md +0 -14
  87. package/knowledge/decisions/vault-secrets-reach-no-sink-and-pii-stays-out-of-logs-and-webhooks.md +0 -30
  88. package/knowledge/decisions/webhook-gateway-wrappers-are-sessionless-and-gateway-auth-runs-inside-the-handler.md +0 -26
  89. package/knowledge/index.md +0 -14
  90. package/knowledge/questions/does-a-non-literal-remote-addon-auth-get-checked.md +0 -26
  91. package/knowledge/questions/index.md +0 -16
  92. package/knowledge/questions/is-an-incomplete-oauth2-block-dropped-without-a-log.md +0 -23
  93. package/knowledge/questions/should-app-functions-named-like-scenario-instrumentation-be-refused.md +0 -28
  94. package/knowledge/questions/should-the-openapi-document-default-every-operation-to-a-bearer-or-api-key.md +0 -22
  95. package/run-tests.sh +0 -68
  96. package/src/utils/test-data/inspector-state.json +0 -1687
@@ -1,201 +0,0 @@
1
- # Inspector Package Optimization Plan
2
-
3
- ## Context
4
-
5
- The inspector (`packages/inspector/src/`) is a static code analyzer built on TypeScript's compiler API. It extracts metadata from source files via a two-pass AST visitor pattern (setup + routes). Profiling reveals that for **every AST node**, all handlers are called sequentially (10 in setup, 16 in routes) even though only 1 ever matches. This creates millions of wasted function calls on large codebases.
6
-
7
- The inspector already has `performance.now()` instrumentation for 7 phases. We'll use this to measure each optimization independently.
8
-
9
- ---
10
-
11
- ## Step 0: Create Benchmark Harness
12
-
13
- **Goal:** Establish reproducible baseline measurements.
14
-
15
- **File:** `src/benchmark.ts`
16
-
17
- **What it does:**
18
-
19
- - Collects `.ts` files from a target directory (e.g., the external repo's packages)
20
- - Runs `inspect()` N iterations (default 10) with a warmup run
21
- - Parses existing `logger.debug` messages to capture per-phase timings
22
- - Reports median, mean, min, max for each phase + total
23
- - Outputs JSON for before/after diffing
24
- - Also benchmarks `filterInspectorState()` separately using the test fixture (`src/utils/test-data/inspector-state.json`)
25
- - Adds counters for: total AST nodes visited, CallExpression nodes, dispatch hits
26
-
27
- **Run with:**
28
-
29
- ```bash
30
- node --import tsx src/benchmark.ts <target-dir> <iterations>
31
- ```
32
-
33
- **How to verify:** Run 3x, confirm <5% variance between runs for each phase.
34
-
35
- ---
36
-
37
- ## Step 1: Identifier-Based Dispatch Map in `visit.ts`
38
-
39
- **File:** `src/visit.ts`
40
-
41
- **Problem:** Every AST node goes through ALL handlers. In `visitRoutes`, that's 16 function calls per node. Each handler independently checks `isCallExpression` -> `isIdentifier` -> `expression.text === 'xyz'`. For non-CallExpression nodes (the vast majority), all 16 calls are wasted.
42
-
43
- **Fix:** Check node kind once, extract identifier once, use `Map<string, handler>` for O(1) dispatch.
44
-
45
- **Dispatch map for `visitRoutes`** (all handlers expect CallExpression + Identifier):
46
-
47
- | Identifier | Handler |
48
- | ------------------- | ------------------ |
49
- | `wireHTTP` | `addHTTPRoute` |
50
- | `wireHTTPRoutes` | `addHTTPRoutes` |
51
- | `wireScheduler` | `addSchedule` |
52
- | `wireTrigger` | `addTrigger` |
53
- | `wireTriggerSource` | `addTrigger` |
54
- | `wireQueueWorker` | `addQueueWorker` |
55
- | `wireChannel` | `addChannel` |
56
- | `wireCLI` | `addCLI` |
57
- | `pikkuCLIRender` | `addCLIRenderers` |
58
- | `wireMCPResource` | `addMCPResource` |
59
- | `wireMCPTool` | `addMCPTool` |
60
- | `wireMCPPrompt` | `addMCPPrompt` |
61
- | `wireWorkflowGraph` | `addWorkflowGraph` |
62
- | `defineSecret` | `addSecret` |
63
- | `defineCredential` | `addCredential` |
64
- | `defineScope` | `addScope` |
65
- | `defineVariable` | `addVariable` |
66
-
67
- `addAuth` stays outside the map: it matches several identifiers
68
- (`betterAuthStatelessSession`, `betterAuthSession`) and gates them on
69
- surrounding context rather than on the identifier alone, so it keeps running on
70
- every CallExpression.
71
-
72
- Fallback for unmatched identifiers: check `/pikku.*func/i` regex -> `addFunctions`.
73
-
74
- **Dispatch for `visitSetup`** (mixed node kinds):
75
-
76
- - `SyntaxKind.CallExpression` + Identifier -> map with: `external`, `workflow`, `workflowStart`, `workflowRun`, `workflowStatus`, `graphStart` -> `addRPCInvocations`; `pikkuMiddleware`, `pikkuMiddlewareFactory`, `addMiddleware`, `addHTTPMiddleware` -> `addMiddleware`; `pikkuPermission`, `pikkuPermissionFactory`, `addPermission`, `addHTTPPermission` -> `addPermission`; `pikkuWorkflowFunc`, `pikkuWorkflowComplexFunc` -> `addWorkflow`
77
- - `SyntaxKind.CallExpression` + PropertyAccessExpression (`rpc.invoke`) -> `addRPCInvocations`
78
- - `SyntaxKind.ClassDeclaration` / `InterfaceDeclaration` -> `addFileExtendsCoreType` (4 core type names)
79
- - `SyntaxKind.VariableDeclaration` -> `addFileWithFactory` (3 factory type names)
80
- - All other node kinds -> skip directly to recursion
81
-
82
- **Measure:** Compare "Visit setup phase" and "Visit routes phase" benchmark timings.
83
-
84
- **Expected improvement:** 30-60% reduction in visit phase times.
85
-
86
- **Verify:** `bash run-tests.sh` + compare serialized inspector state output before/after.
87
-
88
- ---
89
-
90
- ## Step 2: Hoist Regex + Remove Redundant Checks in `add-functions.ts`
91
-
92
- **File:** `src/add/add-functions.ts`
93
-
94
- **Problem (lines 290-309):**
95
-
96
- ```ts
97
- // Line 301: regex re-created EVERY call (every AST node)
98
- const pikkuFuncPattern = /pikku.*func/i
99
- // Line 307: redundant - isIdentifier already checked at line 296,
100
- // startsWith('pikku') is a subset of the regex
101
- if (!ts.isIdentifier(expression) || !expression.text.startsWith('pikku'))
102
- ```
103
-
104
- **Fix:**
105
-
106
- 1. Move `const pikkuFuncPattern = /pikku.*func/i` to module scope
107
- 2. Remove the redundant check at line 307-309
108
-
109
- **Measure:** "Visit routes phase" timing.
110
-
111
- **Expected improvement:** 1-5% of visit routes phase.
112
-
113
- **Verify:** `bash run-tests.sh`
114
-
115
- ---
116
-
117
- ## Step 3: Replace `getText()` with `.text` Property Access
118
-
119
- **Files:** 3-5 files, ~8 safe replacements
120
-
121
- `node.getText()` reconstructs text by slicing the source string. For `Identifier` nodes, `.text` is a direct property -- zero overhead.
122
-
123
- **Safe replacements:**
124
-
125
- - `src/add/add-file-extends-core-type.ts` line 13: `node.name?.getText()` -> `node.name?.text`
126
- - `src/add/add-file-with-factory.ts` line 24: `node.name.getText()` -> guard with `ts.isIdentifier(node.name) ? node.name.text : node.name.getText()`
127
- - `src/add/add-channel.ts` line 360: `shorthandDecl.name.getText()` -> `.text` (ImportSpecifier name is always Identifier)
128
-
129
- **Skip** (not safe or rarely hit): error messages, template literals, TypeNode, QualifiedName.
130
-
131
- **Measure:** "Visit setup phase" + "Visit routes phase" timings.
132
-
133
- **Expected improvement:** 1-3% of visit phases.
134
-
135
- **Verify:** `bash run-tests.sh`
136
-
137
- ---
138
-
139
- ## Step 4: Replace `JSON.parse(JSON.stringify(...))` with `structuredClone()`
140
-
141
- **File:** `src/utils/filter-inspector-state.ts` (lines 198-233)
142
-
143
- **Problem:** 8 calls to `JSON.parse(JSON.stringify(...))` for deep cloning metadata objects. This serializes to string then parses back -- wasteful.
144
-
145
- **Fix:** Replace all 8 with `structuredClone()` (available Node 17+, package requires >=18).
146
-
147
- ```ts
148
- // Before
149
- meta: JSON.parse(JSON.stringify(state.http.meta))
150
- // After
151
- meta: structuredClone(state.http.meta)
152
- ```
153
-
154
- **Measure:** Separate `filterInspectorState` benchmark loop using the 45KB test fixture.
155
-
156
- **Expected improvement:** 10-30% of filter phase time (not the main inspect path, but important for CLI).
157
-
158
- **Verify:** `bash run-tests.sh` (filter-inspector-state.test.ts covers this extensively -- 1,440 lines of tests).
159
-
160
- ---
161
-
162
- ## Step 5: Cache `node.getSourceFile().fileName` in Hot Handlers
163
-
164
- **Files:** Primarily `src/add/add-functions.ts`
165
-
166
- **Problem:** `add-functions.ts` calls `node.getSourceFile().fileName` 4+ times within a single handler invocation for the same node (lines 666, 700, 721, 735). `getSourceFile()` walks the parent chain each time.
167
-
168
- **Fix:** Extract once at handler entry:
169
-
170
- ```ts
171
- const sourceFileName = node.getSourceFile().fileName
172
- ```
173
-
174
- Then reuse `sourceFileName` throughout.
175
-
176
- **Measure:** "Visit routes phase" timing.
177
-
178
- **Expected improvement:** 1-2%.
179
-
180
- **Verify:** `bash run-tests.sh`
181
-
182
- ---
183
-
184
- ## Summary
185
-
186
- | Step | Change | File(s) | Expected Impact | Metric |
187
- | ---- | ----------------- | --------------------------- | -------------------------- | -------------------- |
188
- | 0 | Benchmark harness | New `benchmark.ts` | Baseline | - |
189
- | 1 | Dispatch map | `visit.ts` | **30-60%** of visit phases | Visit setup + routes |
190
- | 2 | Hoist regex | `add-functions.ts` | 1-5% of visit routes | Visit routes |
191
- | 3 | getText -> .text | 3-5 files | 1-3% of visit phases | Visit setup + routes |
192
- | 4 | structuredClone | `filter-inspector-state.ts` | 10-30% of filter phase | Filter benchmark |
193
- | 5 | Cache sourceFile | `add-functions.ts` | 1-2% of visit routes | Visit routes |
194
-
195
- ## Verification
196
-
197
- After each step:
198
-
199
- 1. Run benchmark: `node --import tsx src/benchmark.ts <target> 10`
200
- 2. Run tests: `cd packages/inspector && bash run-tests.sh`
201
- 3. Compare serialized inspector state output (before vs after) to confirm identical results
@@ -1,22 +0,0 @@
1
- ---
2
- type: decision
3
- title: A channel with auth false marks its connect and disconnect handlers sessionless
4
- description: The inspector synthesises metadata for a channel's connect and disconnect functions and sets sessionless from the channel's auth
5
- tags: [inspector, channels, auth]
6
- ---
7
-
8
- # A channel with auth false marks its connect and disconnect handlers sessionless
9
-
10
- In `packages/inspector/src/add/add-channel.ts`, `channelAuth` is read from the channel's
11
- `auth` property (line 705). `sessionless` is `true` only when that value is `false`
12
- (line 708). Otherwise it is `undefined`.
13
-
14
- For each of the connect and disconnect function ids (line 709), the inspector creates a
15
- metadata entry with that `sessionless` value when none exists yet (lines 710-717). An entry
16
- that already exists is left alone (line 710).
17
-
18
- The comment at lines 701-704 says these handlers are defined with the channel-specific
19
- helpers and not `pikkuFunc` or `pikkuSessionlessFunc`, so the runtime needs the flag here.
20
-
21
- **What this rules out:** a channel with `auth: false` whose connect or disconnect handler is
22
- still treated as needing a session. The check covers only the channel's own `auth` value.
@@ -1,20 +0,0 @@
1
- ---
2
- type: decision
3
- title: A declared scope id never shadows a definition
4
- description: A bare declareScopes id is added only when no definition already holds it, so an addon's richer scope tree is kept
5
- tags: [inspector, scopes]
6
- ---
7
-
8
- # A declared scope id never shadows a definition
9
-
10
- `addDeclareScopes` in `packages/inspector/src/add/add-scope.ts` records each valid id
11
- in `state.scopes.declared` and keeps the first source file for it (lines 164-167).
12
-
13
- `applyDeclaredScopes` folds those ids into `state.scopes.definitions` later (lines
14
- 178-192). It reuses a root that already exists (lines 181-185). For each further
15
- segment it uses `??=`, so a node that is already there is kept (lines 188-189). The
16
- comments at lines 137-141 and 173-177 say an addon's richer declaration "is never
17
- shadowed by a bare id" and that an id already held "is left alone".
18
-
19
- **What this rules out:** a bare `declareScopes` entry overwriting the richer tree an
20
- addon or `defineScope` provided.
@@ -1,27 +0,0 @@
1
- ---
2
- type: decision
3
- title: A workflow DSL step's virtual user is the literal name written in its options object
4
- description: The DSL extractor reads a step's virtual user from a property access or string-keyed element access in the options literal, and leaves it undefined when the value is not static
5
- tags: inspector, workflow, dsl, virtual-users
6
- ---
7
-
8
- # A workflow DSL step's virtual user is the literal name written in its options object
9
-
10
- `extractVirtualUserFromOptions` in `packages/inspector/src/utils/workflow/dsl/patterns.ts`
11
- returns the virtual user name for a step. Its doc comment says
12
- `{ virtualUser: virtualUsers.x }` gives `'x'`, also `virtualUsers['x']`, and is
13
- `undefined` when the virtual user is absent or "not statically resolvable"
14
- (`patterns.ts:122-126`).
15
-
16
- The function reads the `virtualUser` property of the options object literal
17
- (`patterns.ts:127-150`). A property access returns its name, and a string-keyed
18
- element access returns the string (`patterns.ts:138-146`). Any other form returns
19
- `undefined` (`patterns.ts:149`).
20
-
21
- The DSL extractor calls it for RPC steps (`packages/inspector/src/utils/workflow/dsl/extract-dsl-workflow.ts:790`)
22
- and for scenario steps (`extract-dsl-workflow.ts:853`). The step is still recorded
23
- with `virtualUser` set to `undefined` when the name is not literal.
24
-
25
- **What this rules out:** resolving a step's virtual user from a variable, a computed
26
- key or a runtime value at inspection time. A virtual user the inspector cannot read
27
- statically is left blank, not guessed.
@@ -1,20 +0,0 @@
1
- ---
2
- type: decision
3
- title: A feature flag's anyOf is read from the AST, and an empty anyOf is refused
4
- description: add-feature-flag reads the scope names in anyOf statically, refuses a non-array or empty anyOf, and refuses a flag name containing the scope separator
5
- tags: [inspector, feature-flags, scopes]
6
- ---
7
-
8
- # A feature flag's anyOf is read from the AST, and an empty anyOf is refused
9
-
10
- `extractAnyOf` in `packages/inspector/src/add/add-feature-flag.ts` (lines 96-137) reads the
11
- `anyOf` property. If the property is absent it returns `undefined` (lines 108-110). If it is
12
- present but not an array literal, it reports a critical error (lines 112-121). Each element must be
13
- a string literal (lines 124-133). A non-literal element is reported and skipped.
14
-
15
- In `addFeatureFlag`, a flag name containing the scope separator is refused (lines 193-199). A body
16
- that is not an object literal, or a variable holding one, is refused (lines 204-211). An `anyOf` that
17
- is an empty array is refused (lines 215-221), and the message says to omit `anyOf` for a pure switch.
18
-
19
- **What this rules out:** a flag whose capability list cannot be read from the source, a flag named
20
- like a scope, and `anyOf: []`. An omitted `anyOf` is not refused; it is recorded as `undefined` (line 230).
@@ -1,25 +0,0 @@
1
- ---
2
- type: decision
3
- title: A string array with one computed entry reads as absent, not as its literal entries
4
- description: stringArrayProperty returns undefined unless every element of the array is a string literal
5
- tags: [inspector, literals, virtual-users]
6
- ---
7
-
8
- # A string array with one computed entry reads as absent, not as its literal entries
9
-
10
- `stringArrayProperty` in `packages/inspector/src/utils/literal-properties.ts` returns
11
- `undefined` when the property is missing or is not an array literal (lines 84-86). It keeps
12
- the string-literal entries (lines 87-89) and returns them only when every element was kept
13
- (line 90). One computed entry therefore makes the whole result `undefined`.
14
-
15
- The comment at lines 74-77 says a half-read array "typechecks, runs, and grants less than
16
- the source says", which is why the helper is all-or-nothing.
17
-
18
- Callers in this package include `add-virtual-users.ts` for `roles`, `goals` and `tags`, which
19
- fall back to `[]` when the helper returns `undefined` (lines 248-250), and for `fixtures`
20
- and `environments` (lines 274 and 279). `add-feature.ts` keeps a local copy of the helper with the same all-or-nothing rule
21
- and reads `tags` through it (line 256).
22
- A search of the package found no caller that reads `scopes` through this helper.
23
-
24
- **What this rules out:** a virtual user or feature keeping the literal entries of a roles or tags
25
- array that also holds a computed value. Such a virtual user gets an empty list instead.
@@ -1,20 +0,0 @@
1
- ---
2
- type: decision
3
- title: A permission whose data parameter starts with an underscore takes no data
4
- description: An underscore-prefixed data parameter marks a permission as auth-only, and pikkuAuth always has no data
5
- tags: [inspector, permissions]
6
- ---
7
-
8
- # A permission whose data parameter starts with an underscore takes no data
9
-
10
- In `packages/inspector/src/add/add-permission.ts`, `requiresData` is false when the
11
- second parameter's name starts with `_` (lines 107-110). The permission entry then
12
- gets `requiresData: false` (line 120). The debug log labels such an entry
13
- "auth-only" (line 124).
14
-
15
- `pikkuAuth` always sets `requiresData: false` (line 204) and an empty, optimized wires
16
- list (line 171). Its handler's second parameter is the resolved session, not a wires
17
- bag (comment at lines 167-170).
18
-
19
- **What this rules out:** a permission whose data parameter is underscore-prefixed
20
- being treated as needing request data.
@@ -1,27 +0,0 @@
1
- ---
2
- type: decision
3
- title: A provider-login virtual user is never runnable, even when declared runnable
4
- description: Runnable is derived from the account, so a virtual user with a provider login cannot be driven, and an explicit runnable true does not change that
5
- tags: [inspector, virtual-users]
6
- ---
7
-
8
- # A provider-login virtual user is never runnable, even when declared runnable
9
-
10
- `addVirtualUsers` in `packages/inspector/src/add/add-virtual-users.ts` sets `runnable`
11
- through `isRunnableVirtualUser`, passing the declared `runnable` value and the account
12
- (lines 242-256). It imports the function from `@pikku/core/virtual-user` (line 3).
13
-
14
- The function is in `packages/core/src/wirings/virtual-user/validate-virtual-users.ts`.
15
- An explicit `runnable: false` gives false (lines 23-25). Otherwise the result is
16
- `account?.provider === undefined` (line 26). A declared `runnable: true` does not
17
- override the provider check, so a provider account is never runnable.
18
- `packages/core/src/wirings/virtual-user/virtual-user-declaration.test.ts:89-90` covers
19
- the provider case.
20
-
21
- The doc comment gives the reason (`validate-virtual-users.ts:7-18`). Driving a
22
- provider's consent screen needs a human, so runnability is derived from the account
23
- rather than asked for: "a flag someone must remember to set is a flag that ships
24
- wrong". An explicit `false` marks a person who is only ever acted upon.
25
-
26
- **What this rules out:** a provider-login virtual user being driven by a run,
27
- whatever the declaration says.
@@ -1,27 +0,0 @@
1
- ---
2
- type: decision
3
- title: A remote addon's bound credential or secret id must name a wired slot, or validation reports a critical error
4
- description: validateRemoteAddonAuth checks that a literal auth.credentialId or auth.secretId on a wireRemoteAddon names a declared credential or secret, and runs in every inspector pass
5
- tags: inspector, addon, auth, validation
6
- ---
7
-
8
- # A remote addon's bound credential or secret id must name a wired slot, or validation reports a critical error
9
-
10
- `validateRemoteAddonAuth` in `packages/inspector/src/utils/post-process.ts:760` is one of the validators the
11
- inspector runs in every pass that is not setup-only (`packages/inspector/src/inspector.ts:523`). It is called
12
- at `packages/inspector/src/inspector.ts:578`, after the remote addon dependency check and before the scope checks.
13
-
14
- Its doc comment limits what it checks. A `credentialId` must name a wired credential and a `secretId` a
15
- wired secret. A custom `resolve()` and an omitted surface are not statically checkable, so they are left to
16
- runtime (`post-process.ts:754-759`).
17
-
18
- The function returns when no `wireAddon` declarations exist (`post-process.ts:764-765`). It then loops over
19
- the remote declarations (`post-process.ts:771`). A `credentialId` that matches no credential definition
20
- logs a critical `REMOTE_ADDON_AUTH_UNRESOLVED` error that lists the available credentials (`post-process.ts:773-776`).
21
- A `secretId` that matches no secret definition gets the same error with the available secrets (`post-process.ts:779-782`).
22
-
23
- Whether a non-literal `auth` value is checked at all is a separate gap, in
24
- [does a non-literal remote addon auth get checked](../questions/does-a-non-literal-remote-addon-auth-get-checked.md).
25
-
26
- **What this rules out:** a remote addon binding that names a credential or secret the consumer never wires,
27
- passing inspection silently and failing only at runtime.
@@ -1,22 +0,0 @@
1
- ---
2
- type: decision
3
- title: A route's own auth overrides its group's, and auth false on a route makes an inline function sessionless
4
- description: The route's auth is used when it is true or false, the group's inherited value otherwise; a resolved false sets sessionless on an inline function
5
- tags: [inspector, http, auth]
6
- ---
7
-
8
- # A route's own auth overrides its group's, and auth false on a route makes an inline function sessionless
9
-
10
- In `packages/inspector/src/add/add-http-route.ts`, the route's own `auth` is read first
11
- (line 281). `resolvedAuth` takes that value when it is `true` or `false` (lines 282-284),
12
- and otherwise takes `inheritedAuth` (line 285), which the caller passes in from the group.
13
-
14
- The comment at lines 277-280 says the value is recorded on the meta so an audit can tell
15
- whether a route is open without re-reading the source.
16
-
17
- Lines 287-289 say that for inline functions, `sessionless` is set to `true` when auth is
18
- disabled at the route or group level. The code sets it only when the inline function's
19
- `sessionless` is still `undefined` and `resolvedAuth` is `false` (lines 290-298).
20
-
21
- **What this rules out:** a route with `auth: true` inheriting `false` from its group. It also
22
- rules out letting an inline function be marked sessionless while the route asks for auth.
@@ -1,29 +0,0 @@
1
- ---
2
- type: decision
3
- title: A route requires a session when any auth signal says so
4
- description: Four places can demand a session for an HTTP route and they combine with OR; a scope gate counts as a session demand
5
- tags: [inspector, auth, http]
6
- ---
7
-
8
- # A route requires a session when any auth signal says so
9
-
10
- `annotateHttpRouteAuth` in `packages/inspector/src/utils/annotate-http-route-auth.ts`
11
- sets `route.requiresSession` to true when any one of these holds (lines 31-38):
12
-
13
- - the function's `sessionless` is `false`, which is a `pikkuFunc`;
14
- - the function's own `auth` is `true`;
15
- - the function declares one or more `scopes`;
16
- - the route's own `auth` is `true`;
17
- - the route's package is in the set of packages gated by their addon.
18
-
19
- The last signal comes from `packagesGatedByTheirAddon` (lines 44-53). It gates a
20
- package when one of its `wireAddon` declarations has `auth === true` or a
21
- non-empty `scopes` list.
22
-
23
- The scope rule is argued in the file's own doc comment. Scopes are checked
24
- against the session and fail closed, so an anonymous caller is rejected either
25
- way. Not counting them "would mark a route open that nobody can reach anonymously"
26
- (`annotateHttpRouteAuth`, lines 14-16).
27
-
28
- **What this rules out:** reading a scope-gated route as open. It also rules out
29
- letting one signal cancel another; the four places only add demands.
@@ -1,21 +0,0 @@
1
- ---
2
- type: decision
3
- title: A webhook source with a verify step declares a singleton credential for its secret
4
- description: When a webhook source has a verify step, the inspector registers a string credential named by the source unless the author sets credential
5
- tags: [inspector, triggers, webhooks, credentials]
6
- ---
7
-
8
- # A webhook source with a verify step declares a singleton credential for its secret
9
-
10
- In `packages/inspector/src/add/add-trigger.ts`, the code returns early unless the source has a
11
- `verify` initializer (line 283). When it has one, the credential name is the `credential`
12
- property if set, otherwise `webhookSecretCredentialName(name)` (lines 285-287). That helper is
13
- imported from `@pikku/core/trigger` (line 21).
14
-
15
- The inspector then records a credential definition with `type: 'singleton'` and a string schema,
16
- named by that value (lines 288-297). Its display name is `<name> webhook secret` (line 291), and
17
- its description is the `credentialDescription` property when set (lines 292-294).
18
-
19
- **What this rules out:** a verified webhook source whose secret is left out of the credential
20
- declarations. The author can choose a different credential name, but a verify step always
21
- registers one.
@@ -1,21 +0,0 @@
1
- ---
2
- type: decision
3
- title: A wire with auth disabled cannot point at a pikkuFunc
4
- description: A route or wiring with auth false that names a session-requiring pikkuFunc is reported as critical, and the function is not accepted
5
- tags: [inspector, auth, validation]
6
- ---
7
-
8
- # A wire with auth disabled cannot point at a pikkuFunc
9
-
10
- `validateAuthSessionless` in `packages/inspector/src/utils/validate-auth-sessionless.ts`
11
- resolves the wire's auth. The wire's own `auth` wins when it is a boolean; otherwise
12
- the inherited value is used (lines 18-20).
13
-
14
- When that resolves to `false` and the function's meta has `sessionless === false`,
15
- the function logs `AUTH_DISABLED_REQUIRES_SESSIONLESS` as critical and returns
16
- `false` (lines 21-27). The message tells the author to use `pikkuSessionlessFunc`
17
- instead (line 24).
18
-
19
- **What this rules out:** a wire that declares no session needed while its function
20
- does need one. The check runs on the wire's auth, not on the function's own `auth`
21
- field.
@@ -1,25 +0,0 @@
1
- ---
2
- type: decision
3
- title: Addons may not call wiring helpers or declare middleware or permissions
4
- description: Addon packages are banned from transport wirings, and their define* contracts may not carry middleware or permissions
5
- tags: [inspector, addon]
6
- ---
7
-
8
- # Addons may not call wiring helpers or declare middleware or permissions
9
-
10
- `checkAddonBans` in `packages/inspector/src/add/add-addon-bans.ts` runs only when the
11
- package is inspected as an addon (line 63). It enforces two rules.
12
-
13
- - A call to any name in `BANNED_WIRINGS` is critical `ADDON_WIRING_NOT_ALLOWED`
14
- (lines 10-24, 68-74). The list includes `wireAddon`, `wireRemoteAddon`, `wireHTTP`
15
- and `wireChannel`.
16
- - A `defineHTTPRoutes`, `defineChannelRoutes` or `defineCLICommands` call whose
17
- first argument has a `middleware` or `permissions` property is critical
18
- `ADDON_CONTRACT_HANDLERS_NOT_ALLOWED` (lines 26-30, 32-49, 76-83).
19
-
20
- The file comment (lines 6-8) says addons "declare contracts with the define*
21
- helpers and export functions", and the consuming app wires them. The comment at
22
- lines 52-54 says middleware and permissions "are the consuming app's concern".
23
-
24
- **What this rules out:** an addon fixing its own transports, or attaching its own
25
- access rules to the routes it contributes.
@@ -1,19 +0,0 @@
1
- ---
2
- type: decision
3
- title: An addon declaration names the secrets and credentials the app lends it
4
- description: secretGrants and credentialGrants are declared on the addon instance as lists of names the addon reads
5
- tags: [inspector, addons, grants]
6
- ---
7
-
8
- # An addon declaration names the secrets and credentials the app lends it
9
-
10
- The addon declaration type in `packages/inspector/src/types.ts` has `secretGrants?: string[]` and
11
- `credentialGrants?: string[]`, each commented as the secrets or credentials "the app lends this
12
- instance, named as the addon reads them" (lines 757-760).
13
-
14
- In `packages/inspector/src/add/add-wire-addon.ts`, those two properties are read with
15
- `parseStringArray` (lines 212-215). The helper itself is not read here, so this note does not say how
16
- it treats a non-literal entry.
17
-
18
- **What this rules out:** an addon instance receiving secrets or credentials that the app did not name in
19
- its `wireAddon` call. The names are the grant.
@@ -1,23 +0,0 @@
1
- ---
2
- type: decision
3
- title: An inline wiring function is sessionless unless its helper is pikkuFunc
4
- description: The inspector derives sessionless for a function inlined into a wiring from the name of the helper that built it
5
- tags: [inspector, functions, auth]
6
- ---
7
-
8
- # An inline wiring function is sessionless unless its helper is pikkuFunc
9
-
10
- `ensureInlineWiringFunction` in `packages/inspector/src/utils/ensure-function-metadata.ts`
11
- registers metadata for a function written inline in a wiring (lines 329-344). The comment at
12
- lines 324-327 says the stub would otherwise carry no session info, and the runtime would then
13
- require a session for every call.
14
-
15
- After that, if the metadata's `sessionless` is still `undefined` (line 349) and the initializer
16
- is a call whose callee is a plain identifier (lines 350-351), `sessionless` is set to
17
- `callee !== 'pikkuFunc'` (line 353). The comment at lines 324-327 gives the same rule as the
18
- one `addFunctions` applies to named functions.
19
-
20
- If the callee is not a plain identifier, `sessionless` is left `undefined`.
21
-
22
- **What this rules out:** treating an inline wiring function as session-required because it was
23
- built by a helper other than `pikkuFunc`.
@@ -1,24 +0,0 @@
1
- ---
2
- type: decision
3
- title: better-auth's admin plugin is refused in favour of pikkuBan
4
- description: Wiring admin() in pikkuBetterAuth throws at build time, and the error names pikkuBan() as the replacement
5
- tags: [inspector, auth, better-auth]
6
- ---
7
-
8
- # better-auth's admin plugin is refused in favour of pikkuBan
9
-
10
- In `packages/inspector/src/add/add-auth.ts`, a plugin named `admin` inside
11
- `pikkuBetterAuth` throws rather than warning (lines 342-344). The error text is
12
- `UNSUPPORTED_ADMIN_PLUGIN` (lines 174-190). It tells the app to use `pikkuBan()`.
13
-
14
- The doc comment above the constant (lines 158-172) gives the reason. `admin()`
15
- authorizes against a `user.role` column, and pikku authorizes on scopes. Running
16
- both means projecting one onto the other, coarsely. The comment says user
17
- management now lives in `@pikku/addon-admin`, and the ban refusal lives in
18
- `pikkuBan()`.
19
-
20
- The comment at lines 170-172 says the throw is deliberate: "the failure it prevents
21
- is silent". An app that drops `admin()` without wiring `pikkuBan()` keeps its ban
22
- columns and stops enforcing bans.
23
-
24
- **What this rules out:** `admin()` as a second authorization model next to scopes.
@@ -1,18 +0,0 @@
1
- ---
2
- type: decision
3
- title: A define-screens scopes list must be string literals, or the build reports a critical error
4
- description: add-define-screens reads each screen's scopes by AST and reports ADDON_SCREENS_NOT_STATIC when the value is not an array of string literals
5
- tags: [inspector, screens, scopes]
6
- ---
7
-
8
- # A define-screens scopes list must be string literals, or the build reports a critical error
9
-
10
- In `packages/inspector/src/add/add-define-screens.ts`, each screen's `scopes` value is read with
11
- `stringArray(value)` (line 110). If that returns nothing, `fail(...)` runs (lines 111-115), with the
12
- message "A screen's scopes must be an array of string literals".
13
-
14
- `fail` is defined at lines 60-61 as `logger.critical(ErrorCode.ADDON_SCREENS_NOT_STATIC, message)`.
15
- The same block requires `component` to be a literal lazy import (lines 116-120).
16
-
17
- **What this rules out:** a screen whose scopes come from a variable, a computed value or a call.
18
- The inspector reads scopes statically, so it cannot check a value it cannot see.