@pikku/inspector 0.12.68 → 0.12.70

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/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ ## 0.12.70
2
+
3
+ ### Patch Changes
4
+
5
+ - d64db40: Prune a filtered-out workflow's file entry, not just its meta.
6
+
7
+ `filterInspectorState` deleted a pruned workflow from `workflows.graphMeta` and `workflows.meta` but left it in `workflows.files`, which is what the wiring generator iterates. Per-unit deploy codegen therefore registered every workflow in the project into every unit, and — because a scenario is told apart from an app workflow by `graphMeta[name].source === 'scenario'`, which the same prune had just removed — each scenario was reclassified as an app workflow. That pulled scenario bodies, their steps, and whatever those import into deployed bundles; on Cloudflare a step reading a fixture off disk failed the whole publish with `No such module "node:fs"`.
8
+
9
+ ## 0.12.69
10
+
11
+ ### Patch Changes
12
+
13
+ - 32d1280: Prefix the better-auth plugin factories with `pikku`: `pikkuActor`, `pikkuBan`,
14
+ `pikkuFabric`, `pikkuDelegatedAuth` and `pikkuCredentialOAuth`.
15
+
16
+ A `betterAuth({ plugins: [...] })` array mixes this package's plugins with
17
+ better-auth's own, and until now nothing at the call site told them apart —
18
+ `plugins: [actor(...), ban(), fabric(...), organization()]` reads as four
19
+ plugins from one place when only the last is better-auth's. The prefix says
20
+ which package a plugin came from where it is actually wired.
21
+
22
+ The old names are still exported as deprecated aliases bound to the same
23
+ functions, so no import has to change. Nothing about the plugins themselves
24
+ moved: the `id` each registers under — `pikku-ban`, `actor`, `fabric`,
25
+ `delegated-auth`, `credential-oauth` — is unchanged, so no deployed database or
26
+ session is affected.
27
+
28
+ The pieces that read a plugin's _export_ name rather than its id accept both:
29
+ `PLUGIN_REGISTRY` is keyed under the prefixed and the bare name, and the
30
+ `pikku validate` ban/actor checks and the `scaffold.userAdmin` ban check count
31
+ either spelling as wired. Their messages now point at the new names.
32
+
33
+ - Updated dependencies [a0ed1e8]
34
+ - @pikku/core@0.12.100
35
+
1
36
  ## 0.12.68
2
37
 
3
38
  ### Patch Changes
@@ -86,7 +86,7 @@ const readPluginBindings = (source) => {
86
86
  };
87
87
  /**
88
88
  * Read a `plugins: [...]` entry. better-auth plugins are factory calls —
89
- * `bearer()`, `twoFactor({ ... })`, `ban()`, or `plugins.bearer()` through a
89
+ * `bearer()`, `twoFactor({ ... })`, `pikkuBan()`, or `plugins.bearer()` through a
90
90
  * namespace import; non-call entries are ignored. `betterAuthExport` is the
91
91
  * name better-auth exports the plugin under, and is absent for a plugin that
92
92
  * came from somewhere else, which no policy here applies to.
@@ -148,10 +148,10 @@ const isInsideGlobalMiddlewareRegistration = (node) => {
148
148
  * every one of its endpoints underneath. Pikku dropped that projection: user
149
149
  * management is `@pikku/addon-admin`'s scoped RPCs, and the one capability
150
150
  * `admin()` had that nothing else did — refusing a session to a banned user —
151
- * is `ban()`.
151
+ * is `pikkuBan()`.
152
152
  *
153
153
  * Thrown rather than warned because the failure it prevents is silent. An app
154
- * that drops `admin()` without wiring `ban()` keeps its ban columns and its ban
154
+ * that drops `admin()` without wiring `pikkuBan()` keeps its ban columns and its
155
155
  * UI, and simply stops enforcing bans.
156
156
  */
157
157
  const UNSUPPORTED_ADMIN_PLUGIN = (sourceFile) => `better-auth's admin() plugin is not supported by pikku (${sourceFile}).
@@ -160,12 +160,12 @@ It authorizes against a 'user.role' column, while pikku authorizes on scopes —
160
160
  wiring both means one has to be projected onto the other, and the projection is
161
161
  coarser than the scopes it stands in for.
162
162
 
163
- Use ban() instead:
163
+ Use pikkuBan() instead:
164
164
 
165
- import { ban } from '@pikku/better-auth'
166
- betterAuth({ plugins: [ban()] })
165
+ import { pikkuBan } from '@pikku/better-auth'
166
+ betterAuth({ plugins: [pikkuBan()] })
167
167
 
168
- ban() keeps the part of admin() that pikku cannot supply from outside
168
+ pikkuBan() keeps the part of admin() that pikku cannot supply from outside
169
169
  better-auth — the banned/banReason/banExpires columns and the session hook that
170
170
  refuses a banned user a session. Everything else admin() offered is already
171
171
  scoped RPCs in @pikku/addon-admin: list, create, ban, remove, revoke sessions
@@ -2,6 +2,23 @@ import { parseVersionedId } from '@pikku/core/utils';
2
2
  import { makeContextBasedId } from './extract-function-name.js';
3
3
  import { aggregateRequiredServices } from './post-process.js';
4
4
  import { IncompatibleDeployTargetError, resolveDeployTarget, } from './resolve-deploy-target.js';
5
+ /**
6
+ * Drop a workflow from every map that describes it. `files` and `graphFiles`
7
+ * are what the wiring generator iterates, and `graphMeta` is what tells a
8
+ * scenario apart from an app workflow — leaving a pruned workflow's file entry
9
+ * behind registers it anyway, and a scenario whose graphMeta has gone reads as
10
+ * an app workflow, which is how scenario steps reached deployed bundles.
11
+ */
12
+ const pruneWorkflow = (workflows, name, pikkuFuncId) => {
13
+ delete workflows.graphMeta[name];
14
+ delete workflows.meta[name];
15
+ workflows.files.delete(name);
16
+ workflows.graphFiles.delete(name);
17
+ if (pikkuFuncId) {
18
+ workflows.files.delete(pikkuFuncId);
19
+ workflows.graphFiles.delete(pikkuFuncId);
20
+ }
21
+ };
5
22
  // Module-level Set to track warned groups across multiple filter calls
6
23
  const globalWarnedGroups = new Set();
7
24
  /**
@@ -334,6 +351,8 @@ export function filterInspectorState(state, filters, logger) {
334
351
  ...state.workflows,
335
352
  graphMeta: JSON.parse(JSON.stringify(state.workflows?.graphMeta ?? {})),
336
353
  meta: JSON.parse(JSON.stringify(state.workflows?.meta ?? {})),
354
+ files: new Map(state.workflows?.files ?? []),
355
+ graphFiles: new Map(state.workflows?.graphFiles ?? []),
337
356
  },
338
357
  channels: {
339
358
  ...state.channels,
@@ -768,13 +787,11 @@ export function filterInspectorState(state, filters, logger) {
768
787
  const pikkuFuncId = graphMeta?.pikkuFuncId ?? workflowMeta?.pikkuFuncId;
769
788
  if (pikkuFuncId &&
770
789
  !filteredState.serviceAggregation.usedFunctions.has(pikkuFuncId)) {
771
- delete filteredState.workflows.graphMeta[name];
772
- delete filteredState.workflows.meta[name];
790
+ pruneWorkflow(filteredState.workflows, name, pikkuFuncId);
773
791
  }
774
792
  else if (!pikkuFuncId) {
775
793
  // No function ID found — prune it
776
- delete filteredState.workflows.graphMeta[name];
777
- delete filteredState.workflows.meta[name];
794
+ pruneWorkflow(filteredState.workflows, name, pikkuFuncId);
778
795
  }
779
796
  }
780
797
  // Prune RPC meta to only include entries whose target function survived
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/inspector",
3
- "version": "0.12.68",
3
+ "version": "0.12.70",
4
4
  "description": "TypeScript inspector for Pikku — discovers functions and wirings to drive code generation",
5
5
  "author": "yasser.fadl@gmail.com",
6
6
  "license": "BUSL-1.1",
@@ -45,10 +45,10 @@
45
45
  "zod-to-ts": "^2.1.0"
46
46
  },
47
47
  "peerDependencies": {
48
- "@pikku/core": "^0.12.99"
48
+ "@pikku/core": "^0.12.100"
49
49
  },
50
50
  "devDependencies": {
51
- "@pikku/core": "^0.12.99",
51
+ "@pikku/core": "^0.12.100",
52
52
  "@types/node": "^24.13.3"
53
53
  },
54
54
  "engines": {
@@ -217,7 +217,7 @@ describe('addAuth inspector', () => {
217
217
  }
218
218
  })
219
219
 
220
- test('refuses better-auth admin() and points at ban()', async () => {
220
+ test('refuses better-auth admin() and points at pikkuBan()', async () => {
221
221
  const rootDir = await mkdtemp(join(tmpdir(), 'pikku-add-auth-admin-'))
222
222
  const file = join(rootDir, 'auth.ts')
223
223
 
@@ -241,7 +241,7 @@ describe('addAuth inspector', () => {
241
241
  () => inspect(makeLogger(criticals), [file], { rootDir }),
242
242
  (error: Error) => {
243
243
  assert.match(error.message, /admin\(\) plugin is not supported/)
244
- assert.match(error.message, /ban\(\)/)
244
+ assert.match(error.message, /pikkuBan\(\)/)
245
245
  assert.match(error.message, /@pikku\/addon-admin/)
246
246
  return true
247
247
  }
@@ -128,7 +128,7 @@ const readPluginBindings = (source: ts.SourceFile): PluginBindings => {
128
128
 
129
129
  /**
130
130
  * Read a `plugins: [...]` entry. better-auth plugins are factory calls —
131
- * `bearer()`, `twoFactor({ ... })`, `ban()`, or `plugins.bearer()` through a
131
+ * `bearer()`, `twoFactor({ ... })`, `pikkuBan()`, or `plugins.bearer()` through a
132
132
  * namespace import; non-call entries are ignored. `betterAuthExport` is the
133
133
  * name better-auth exports the plugin under, and is absent for a plugin that
134
134
  * came from somewhere else, which no policy here applies to.
@@ -194,10 +194,10 @@ const isInsideGlobalMiddlewareRegistration = (node: ts.Node): boolean => {
194
194
  * every one of its endpoints underneath. Pikku dropped that projection: user
195
195
  * management is `@pikku/addon-admin`'s scoped RPCs, and the one capability
196
196
  * `admin()` had that nothing else did — refusing a session to a banned user —
197
- * is `ban()`.
197
+ * is `pikkuBan()`.
198
198
  *
199
199
  * Thrown rather than warned because the failure it prevents is silent. An app
200
- * that drops `admin()` without wiring `ban()` keeps its ban columns and its ban
200
+ * that drops `admin()` without wiring `pikkuBan()` keeps its ban columns and its
201
201
  * UI, and simply stops enforcing bans.
202
202
  */
203
203
  const UNSUPPORTED_ADMIN_PLUGIN = (sourceFile: string): string =>
@@ -207,12 +207,12 @@ It authorizes against a 'user.role' column, while pikku authorizes on scopes —
207
207
  wiring both means one has to be projected onto the other, and the projection is
208
208
  coarser than the scopes it stands in for.
209
209
 
210
- Use ban() instead:
210
+ Use pikkuBan() instead:
211
211
 
212
- import { ban } from '@pikku/better-auth'
213
- betterAuth({ plugins: [ban()] })
212
+ import { pikkuBan } from '@pikku/better-auth'
213
+ betterAuth({ plugins: [pikkuBan()] })
214
214
 
215
- ban() keeps the part of admin() that pikku cannot supply from outside
215
+ pikkuBan() keeps the part of admin() that pikku cannot supply from outside
216
216
  better-auth — the banned/banReason/banExpires columns and the session hook that
217
217
  refuses a banned user a session. Everything else admin() offered is already
218
218
  scoped RPCs in @pikku/addon-admin: list, create, ban, remove, revoke sessions
@@ -2010,3 +2010,56 @@ describe('invokedFunctionsByFile serialization', () => {
2010
2010
  assert.strictEqual(restored.rpc.invokedFunctionsByFile.size, 0)
2011
2011
  })
2012
2012
  })
2013
+
2014
+ describe('Workflow file pruning', () => {
2015
+ const workflowState = () => {
2016
+ const state = createMockInspectorState()
2017
+ state.workflows.meta = {
2018
+ moveOut: { pikkuFuncId: 'getUsers' } as any,
2019
+ theMoveOutScenario: { pikkuFuncId: 'createUser' } as any,
2020
+ }
2021
+ state.workflows.graphMeta = {
2022
+ moveOut: { pikkuFuncId: 'getUsers' } as any,
2023
+ theMoveOutScenario: {
2024
+ pikkuFuncId: 'createUser',
2025
+ source: 'scenario',
2026
+ } as any,
2027
+ }
2028
+ state.workflows.files = new Map([
2029
+ [
2030
+ 'moveOut',
2031
+ {
2032
+ path: '/test/project/src/wirings/move-out.workflow.ts',
2033
+ exportedName: 'moveOut',
2034
+ },
2035
+ ],
2036
+ [
2037
+ 'theMoveOutScenario',
2038
+ {
2039
+ path: '/test/project/test/scenarios/the-move-out.scenario.ts',
2040
+ exportedName: 'theMoveOutScenario',
2041
+ },
2042
+ ],
2043
+ ])
2044
+ return state
2045
+ }
2046
+
2047
+ test('drops the file entry of a pruned workflow', () => {
2048
+ const result = filterInspectorState(
2049
+ workflowState(),
2050
+ { names: ['getUsers'] },
2051
+ mockLogger
2052
+ )
2053
+
2054
+ assert.deepEqual(Object.keys(result.workflows.graphMeta), ['moveOut'])
2055
+ assert.deepEqual([...result.workflows.files.keys()], ['moveOut'])
2056
+ })
2057
+
2058
+ test('leaves the original state untouched', () => {
2059
+ const state = workflowState()
2060
+ filterInspectorState(state, { names: ['getUsers'] }, mockLogger)
2061
+
2062
+ assert.equal(state.workflows.files.size, 2)
2063
+ assert.equal(Object.keys(state.workflows.graphMeta).length, 2)
2064
+ })
2065
+ })
@@ -21,6 +21,28 @@ type DeployFilter = {
21
21
  has(pikkuFuncId: string): boolean
22
22
  }
23
23
 
24
+ /**
25
+ * Drop a workflow from every map that describes it. `files` and `graphFiles`
26
+ * are what the wiring generator iterates, and `graphMeta` is what tells a
27
+ * scenario apart from an app workflow — leaving a pruned workflow's file entry
28
+ * behind registers it anyway, and a scenario whose graphMeta has gone reads as
29
+ * an app workflow, which is how scenario steps reached deployed bundles.
30
+ */
31
+ const pruneWorkflow = (
32
+ workflows: InspectorState['workflows'],
33
+ name: string,
34
+ pikkuFuncId: string | undefined
35
+ ) => {
36
+ delete workflows.graphMeta[name]
37
+ delete workflows.meta[name]
38
+ workflows.files.delete(name)
39
+ workflows.graphFiles.delete(name)
40
+ if (pikkuFuncId) {
41
+ workflows.files.delete(pikkuFuncId)
42
+ workflows.graphFiles.delete(pikkuFuncId)
43
+ }
44
+ }
45
+
24
46
  // Module-level Set to track warned groups across multiple filter calls
25
47
  const globalWarnedGroups = new Set<string>()
26
48
 
@@ -449,6 +471,8 @@ export function filterInspectorState(
449
471
  ...state.workflows,
450
472
  graphMeta: JSON.parse(JSON.stringify(state.workflows?.graphMeta ?? {})),
451
473
  meta: JSON.parse(JSON.stringify(state.workflows?.meta ?? {})),
474
+ files: new Map(state.workflows?.files ?? []),
475
+ graphFiles: new Map(state.workflows?.graphFiles ?? []),
452
476
  },
453
477
  channels: {
454
478
  ...state.channels,
@@ -1098,12 +1122,10 @@ export function filterInspectorState(
1098
1122
  pikkuFuncId &&
1099
1123
  !filteredState.serviceAggregation.usedFunctions.has(pikkuFuncId)
1100
1124
  ) {
1101
- delete filteredState.workflows.graphMeta[name]
1102
- delete filteredState.workflows.meta[name]
1125
+ pruneWorkflow(filteredState.workflows, name, pikkuFuncId)
1103
1126
  } else if (!pikkuFuncId) {
1104
1127
  // No function ID found — prune it
1105
- delete filteredState.workflows.graphMeta[name]
1106
- delete filteredState.workflows.meta[name]
1128
+ pruneWorkflow(filteredState.workflows, name, pikkuFuncId)
1107
1129
  }
1108
1130
  }
1109
1131