@pikku/inspector 0.12.74 → 0.12.81
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 +347 -0
- package/dist/add/add-analytics.d.ts +9 -0
- package/dist/add/add-analytics.js +178 -0
- package/dist/add/add-auth.js +0 -54
- package/dist/add/add-feature-flag.d.ts +2 -0
- package/dist/add/add-feature-flag.js +162 -0
- package/dist/add/add-wire-addon.js +1 -0
- package/dist/add/add-wire-remote-addon.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/inspector.js +4 -0
- package/dist/surface/collect-surface.d.ts +58 -0
- package/dist/surface/collect-surface.js +613 -0
- package/dist/types.d.ts +20 -11
- package/dist/utils/filter-inspector-state.js +23 -13
- package/dist/utils/post-process.js +15 -0
- package/dist/utils/read-module-specifiers.d.ts +8 -0
- package/dist/utils/read-module-specifiers.js +23 -0
- package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
- package/dist/utils/read-tsconfig-out-dir.js +12 -0
- package/dist/utils/resolve-deploy-target.d.ts +7 -0
- package/dist/utils/resolve-deploy-target.js +15 -7
- package/dist/utils/serialize-inspector-state.d.ts +5 -0
- package/dist/utils/serialize-inspector-state.js +8 -0
- package/dist/visit.js +4 -0
- package/package.json +16 -6
- package/run-tests.sh +21 -6
- package/src/add/add-addon-bans.ts +0 -85
- package/src/add/add-agent-scorer.test.ts +0 -197
- package/src/add/add-agent-scorer.ts +0 -83
- package/src/add/add-agent.test.ts +0 -87
- package/src/add/add-agent.ts +0 -638
- package/src/add/add-approval-description.ts +0 -76
- package/src/add/add-auth.test.ts +0 -791
- package/src/add/add-auth.ts +0 -424
- package/src/add/add-channel.ts +0 -778
- package/src/add/add-cli-renderers.test.ts +0 -74
- package/src/add/add-cli.ts +0 -1006
- package/src/add/add-credential.ts +0 -186
- package/src/add/add-feature.test.ts +0 -306
- package/src/add/add-feature.ts +0 -253
- package/src/add/add-file-extends-core-type.ts +0 -66
- package/src/add/add-file-with-config.ts +0 -45
- package/src/add/add-file-with-factory.test.ts +0 -134
- package/src/add/add-file-with-factory.ts +0 -288
- package/src/add/add-functions.test.ts +0 -743
- package/src/add/add-functions.ts +0 -1548
- package/src/add/add-gateway.ts +0 -143
- package/src/add/add-http-route-ref.test.ts +0 -149
- package/src/add/add-http-route.ts +0 -530
- package/src/add/add-http-routes.ts +0 -471
- package/src/add/add-keyed-wiring.ts +0 -180
- package/src/add/add-mcp-prompt.ts +0 -157
- package/src/add/add-mcp-resource.ts +0 -175
- package/src/add/add-middleware.test.ts +0 -196
- package/src/add/add-middleware.ts +0 -879
- package/src/add/add-permission-auth.test.ts +0 -59
- package/src/add/add-permission.ts +0 -311
- package/src/add/add-personas.test.ts +0 -406
- package/src/add/add-personas.ts +0 -287
- package/src/add/add-queue-worker.ts +0 -117
- package/src/add/add-rpc-invocations.test.ts +0 -287
- package/src/add/add-rpc-invocations.ts +0 -221
- package/src/add/add-scenario-step.test.ts +0 -509
- package/src/add/add-schedule.ts +0 -120
- package/src/add/add-scope.test.ts +0 -419
- package/src/add/add-scope.ts +0 -221
- package/src/add/add-secret.ts +0 -10
- package/src/add/add-subject-scenario-steps.test.ts +0 -161
- package/src/add/add-system-role.test.ts +0 -297
- package/src/add/add-system-role.ts +0 -162
- package/src/add/add-trigger.ts +0 -165
- package/src/add/add-variable.ts +0 -10
- package/src/add/add-wire-addon.test.ts +0 -185
- package/src/add/add-wire-addon.ts +0 -146
- package/src/add/add-wire-remote-addon.ts +0 -80
- package/src/add/add-workflow-edge-cases.test.ts +0 -201
- package/src/add/add-workflow-fanout.test.ts +0 -166
- package/src/add/add-workflow-graph-foreach.test.ts +0 -174
- package/src/add/add-workflow-graph-input.test.ts +0 -94
- package/src/add/add-workflow-graph-notes.test.ts +0 -74
- package/src/add/add-workflow-graph.ts +0 -644
- package/src/add/add-workflow-on-error.test.ts +0 -118
- package/src/add/add-workflow-return-switch.test.ts +0 -121
- package/src/add/add-workflow-silent-drops.test.ts +0 -261
- package/src/add/add-workflow-sleep-expression.test.ts +0 -96
- package/src/add/add-workflow.test.ts +0 -155
- package/src/add/add-workflow.ts +0 -482
- package/src/add/addon-bans.test.ts +0 -121
- package/src/add/addon-contracts.test.ts +0 -221
- package/src/add/allow-escape-hatches.test.ts +0 -144
- package/src/add/audit-meta.test.ts +0 -75
- package/src/add/classified-sink-check.test.ts +0 -347
- package/src/add/config-value-metadata.test.ts +0 -263
- package/src/add/dynamic-import-check.test.ts +0 -119
- package/src/add/expect-eventually-scenario-only.test.ts +0 -108
- package/src/add/function-body-span.test.ts +0 -96
- package/src/add/inline-wiring-function.test.ts +0 -104
- package/src/add/pii-check.test.ts +0 -258
- package/src/add/rpc-type-cast.test.ts +0 -123
- package/src/add/scenario-instrumentation.test.ts +0 -89
- package/src/add/scenario-instrumentation.ts +0 -42
- package/src/add/scenario-marker.test.ts +0 -89
- package/src/add/scenario-not-extractable.test.ts +0 -159
- package/src/add/scenario-services.test.ts +0 -103
- package/src/add/scenario-skip.test.ts +0 -83
- package/src/add/validate-workflow-graph-addons.test.ts +0 -102
- package/src/add/wire-name-literal.test.ts +0 -117
- package/src/error-codes-emitted.test.ts +0 -76
- package/src/error-codes.ts +0 -145
- package/src/index.ts +0 -66
- package/src/inspector.ts +0 -575
- package/src/schema-generator.ts +0 -1
- package/src/source-file-cache.ts +0 -73
- package/src/tsconfig-paths.test.ts +0 -90
- package/src/types-map.ts +0 -141
- package/src/types.ts +0 -775
- package/src/utils/annotate-http-route-auth.test.ts +0 -161
- package/src/utils/annotate-http-route-auth.ts +0 -53
- package/src/utils/check-classified-sinks.ts +0 -215
- package/src/utils/check-pii-output.ts +0 -120
- package/src/utils/compute-diagnostics.test.ts +0 -106
- package/src/utils/compute-required-schemas.ts +0 -48
- package/src/utils/contract-hashes.test.ts +0 -553
- package/src/utils/contract-hashes.ts +0 -387
- package/src/utils/custom-types-generator.test.ts +0 -124
- package/src/utils/custom-types-generator.ts +0 -150
- package/src/utils/detect-schema-vendor.test.ts +0 -123
- package/src/utils/detect-schema-vendor.ts +0 -120
- package/src/utils/does-type-extend-core-type.ts +0 -53
- package/src/utils/ensure-function-metadata.ts +0 -385
- package/src/utils/extract-function-name.ts +0 -612
- package/src/utils/extract-function-node.ts +0 -70
- package/src/utils/extract-node-value.test.ts +0 -117
- package/src/utils/extract-node-value.ts +0 -172
- package/src/utils/extract-secret-usage.test.ts +0 -81
- package/src/utils/extract-secret-usage.ts +0 -73
- package/src/utils/extract-services.ts +0 -70
- package/src/utils/extract-surface-usage.test.ts +0 -147
- package/src/utils/extract-surface-usage.ts +0 -121
- package/src/utils/filter-inspector-state.test.ts +0 -2207
- package/src/utils/filter-inspector-state.ts +0 -1302
- package/src/utils/filter-utils.test.ts +0 -843
- package/src/utils/filter-utils.ts +0 -161
- package/src/utils/find-root-dir.ts +0 -68
- package/src/utils/find-zod-transform.test.ts +0 -70
- package/src/utils/find-zod-transform.ts +0 -103
- package/src/utils/get-exported-variable-name.ts +0 -48
- package/src/utils/get-files-and-methods.ts +0 -157
- package/src/utils/get-property-value.test.ts +0 -141
- package/src/utils/get-property-value.ts +0 -331
- package/src/utils/hash.ts +0 -26
- package/src/utils/literal-properties.ts +0 -97
- package/src/utils/load-addon-functions-meta.test.ts +0 -157
- package/src/utils/load-addon-functions-meta.ts +0 -508
- package/src/utils/load-addon-scopes.test.ts +0 -138
- package/src/utils/middleware.test.ts +0 -204
- package/src/utils/middleware.ts +0 -303
- package/src/utils/nested-project-filter.test.ts +0 -53
- package/src/utils/nested-project-filter.ts +0 -28
- package/src/utils/permissions-shorthand.test.ts +0 -79
- package/src/utils/permissions.test.ts +0 -125
- package/src/utils/permissions.ts +0 -129
- package/src/utils/post-process.test.ts +0 -1094
- package/src/utils/post-process.ts +0 -1604
- package/src/utils/resolve-addon-package.ts +0 -106
- package/src/utils/resolve-core-type.test.ts +0 -142
- package/src/utils/resolve-core-type.ts +0 -35
- package/src/utils/resolve-deploy-target.test.ts +0 -135
- package/src/utils/resolve-deploy-target.ts +0 -65
- package/src/utils/resolve-function-meta.ts +0 -25
- package/src/utils/resolve-function-types.ts +0 -42
- package/src/utils/resolve-identifier.ts +0 -46
- package/src/utils/resolve-ref-contract.ts +0 -71
- package/src/utils/resolve-versions.test.ts +0 -499
- package/src/utils/resolve-versions.ts +0 -228
- package/src/utils/schema-cache.test.ts +0 -124
- package/src/utils/schema-generator.test.ts +0 -272
- package/src/utils/schema-generator.ts +0 -780
- package/src/utils/secret-alias-services.test.ts +0 -146
- package/src/utils/secret-alias-services.ts +0 -58
- package/src/utils/serialize-inspector-state.ts +0 -742
- package/src/utils/serialize-mcp-json.ts +0 -150
- package/src/utils/serialize-middleware-groups-meta.ts +0 -36
- package/src/utils/serialize-openapi-json.ts +0 -277
- package/src/utils/serialize-permissions-groups-meta.ts +0 -7
- package/src/utils/single-declaration.ts +0 -57
- package/src/utils/type-utils.ts +0 -145
- package/src/utils/validate-auth-sessionless.ts +0 -30
- package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
- package/src/utils/validate-exposed-functions-gated.ts +0 -103
- package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
- package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
- package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
- package/src/utils/workflow/derive-workflow-plan.ts +0 -117
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
- package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
- package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
- package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
- package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
- package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
- package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
- package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
- package/src/utils/workflow/dsl/index.ts +0 -11
- package/src/utils/workflow/dsl/patterns.ts +0 -440
- package/src/utils/workflow/dsl/validation.ts +0 -203
- package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
- package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
- package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
- package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
- package/src/utils/workflow/graph/index.ts +0 -28
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
- package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
- package/src/visit.ts +0 -181
- package/tsconfig.json +0 -19
- package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,350 @@
|
|
|
1
|
+
## 0.12.81
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 78237ac: The console shows feature flags as a launch board, and analytics events as a
|
|
6
|
+
declared catalog.
|
|
7
|
+
|
|
8
|
+
A flag's state is four fields — `enabled`, `rolloutPercent`, `declared`,
|
|
9
|
+
`backed` — and none of them is the question an operator actually asks, which is
|
|
10
|
+
how far this has got. The board answers that directly: every declared flag sits
|
|
11
|
+
in the lane its rollout has reached, `dark` → `rolling` → `live`, with
|
|
12
|
+
`attention` for the flags that are not resolving in a way anybody chose.
|
|
13
|
+
|
|
14
|
+
Attention is evaluated before every other reading, and that ordering is the
|
|
15
|
+
whole point rather than an implementation detail. A flag declared in code with
|
|
16
|
+
no row in the store fails open, and it reports `enabled: true` with no rollout
|
|
17
|
+
limit — byte-identical to a deliberate full launch. Read the fields in the
|
|
18
|
+
obvious order and the one production-severity state on the board renders as the
|
|
19
|
+
healthiest one. `groupFlagsByLane` also returns all four lanes whether or not
|
|
20
|
+
they hold anything, because a board read by shape must not rearrange itself the
|
|
21
|
+
moment a lane empties.
|
|
22
|
+
|
|
23
|
+
Selecting a flag opens its controls beside the board rather than over it: the
|
|
24
|
+
switch, the rollout bucket, and the subjects pinned on or off past that bucket.
|
|
25
|
+
They share one surface because they are read together — "off for everyone except
|
|
26
|
+
these three" is a switch and an override row, and an operator who has to leave
|
|
27
|
+
one screen to see the other is the operator who turns a flag on for everybody by
|
|
28
|
+
mistake. The panel tracks the open flag by name and re-reads it from the list, so
|
|
29
|
+
committing a rollout moves the card and the panel together instead of leaving the
|
|
30
|
+
panel describing the flag as it was before the operator changed it.
|
|
31
|
+
|
|
32
|
+
A board whose flags come from a `FeatureFlagSource` — a provider Pikku reads but
|
|
33
|
+
does not own — says so and offers no controls, rather than offering controls that
|
|
34
|
+
would fail.
|
|
35
|
+
|
|
36
|
+
The analytics screen is a catalog of what the code declares, read from build-time
|
|
37
|
+
meta: the events a client may emit and the props each carries, with every prop
|
|
38
|
+
rendered as the schema source text the declaration wrote. It is deliberately not
|
|
39
|
+
a volume report — nothing here counts what was emitted — and saying which of the
|
|
40
|
+
two it is turned out to be the only design question the screen had.
|
|
41
|
+
|
|
42
|
+
Turning a flag on while it carries no rollout limit now asks first. That press
|
|
43
|
+
is the one irreversible thing the board can do — with no bucket left to hold
|
|
44
|
+
anybody back it admits everyone the flag's capabilities allow — and it used to
|
|
45
|
+
be a single click on a switch. The panel also reports what a mutation is doing
|
|
46
|
+
and what it refused to do: the controls it is waiting on go into a pending
|
|
47
|
+
state, and a failed write raises an inline `<Alert>` rather than reverting in
|
|
48
|
+
silence. The console mounts no notification provider, so the feedback is inline
|
|
49
|
+
where the action is, which is the idiom the rest of the console already uses.
|
|
50
|
+
|
|
51
|
+
`@pikku/mantine`'s theme gains four aliases, and they close a contrast defect
|
|
52
|
+
that predates this board:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
'--mantine-color-orange-light': 'var(--app-surface-warning)',
|
|
56
|
+
'--mantine-color-orange-light-color': 'var(--app-amber)',
|
|
57
|
+
'--mantine-color-teal-light': 'var(--app-surface-success)',
|
|
58
|
+
'--mantine-color-teal-light-color': 'var(--app-green)',
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The theme already documents two colour tiers — TEXT must clear AA, GRAPHIC only
|
|
62
|
+
3:1 — and already pulls Mantine's own variables into that contract rather than
|
|
63
|
+
linting call sites. What it had not covered was Mantine's _light variants_:
|
|
64
|
+
`<Alert color="orange">` renders #d9480f on #ffe8cc at 3.62:1, and
|
|
65
|
+
`<Badge variant="light" color="teal">` at 4.33:1, neither reachable from an
|
|
66
|
+
`--app-*` token because the component never asks for one. The ramp itself is
|
|
67
|
+
deliberately untouched: Mantine conflates `--mantine-color-orange-filled`
|
|
68
|
+
between `c="orange"` text and a `variant="filled"` background, so darkening it
|
|
69
|
+
for light-mode text would make an existing filled badge unreadable in dark mode.
|
|
70
|
+
Eight pre-existing `c="orange"` call sites moved onto the amber TEXT tier in the
|
|
71
|
+
same pass.
|
|
72
|
+
|
|
73
|
+
The analytics catalog is grouped by the file that declares each event, and its
|
|
74
|
+
rows are real buttons rather than table cells carrying a tabindex.
|
|
75
|
+
|
|
76
|
+
`ShellHeaderAction` gains `loading` and `testId`, and `ActionCluster` gains a
|
|
77
|
+
`measurement` flag. `ShellHeader` sizes its actions by rendering them a second
|
|
78
|
+
time off-screen, and that measuring copy was carrying the same `data-testid` and
|
|
79
|
+
`data-help` as the real one — so every action on a page with a collapsing header
|
|
80
|
+
resolved to two elements. The flags board also moved its two header actions from
|
|
81
|
+
`actionsNode` (documented as the non-collapsing escape hatch) onto the structured
|
|
82
|
+
`actions` array, which is what stopped them clipping off the right edge at 400px.
|
|
83
|
+
|
|
84
|
+
- Updated dependencies [78237ac]
|
|
85
|
+
- Updated dependencies [add56d2]
|
|
86
|
+
- @pikku/core@0.12.111
|
|
87
|
+
|
|
88
|
+
## 0.12.80
|
|
89
|
+
|
|
90
|
+
### Patch Changes
|
|
91
|
+
|
|
92
|
+
- 1c9a55a: Give `pikku dev` and `pikku serve` a database-backed flag store and analytics sink.
|
|
93
|
+
|
|
94
|
+
Both followed `agentStorage` and friends everywhere except in being wired: with a
|
|
95
|
+
local database present, `dev` built one for agents, workflows and scopes and left
|
|
96
|
+
feature flags unregistered — which makes every gate resolve open — and analytics
|
|
97
|
+
on the logger fallback. Both are now built from the same `kysely`, and dropped
|
|
98
|
+
with a warning naming `pikku db generate` rather than failing the boot when the
|
|
99
|
+
tables are not there yet.
|
|
100
|
+
|
|
101
|
+
The inspector now counts a `featureFlag:` declaration as needing `featureFlags`
|
|
102
|
+
and a `defineAnalyticsEvents` declaration as needing `analyticsService`, the same
|
|
103
|
+
way a declared scope implies `scopeService`. Neither service is ever destructured
|
|
104
|
+
— the runner reaches both out of the singleton services — so without this the
|
|
105
|
+
declaration was invisible to service aggregation and `pikku db generate` wrote no
|
|
106
|
+
tables for either.
|
|
107
|
+
|
|
108
|
+
- 52eb461: Prune a unit's addon wiring files alongside its addon declarations, so a
|
|
109
|
+
deployment unit no longer imports (and registers) addons whose functions the
|
|
110
|
+
unit filter dropped. `wireAddon` now records the app file that declared it, and
|
|
111
|
+
a file is dropped only when every addon it wires is dropped.
|
|
112
|
+
- 1c9a55a: Feature flags, declared in source and resolved from two independent booleans.
|
|
113
|
+
|
|
114
|
+
`defineFeatureFlags` declares a flag the way `defineScopes` declares a scope —
|
|
115
|
+
the inspector collects it, the CLI emits a `FeatureFlagName` union, and a
|
|
116
|
+
misspelled `featureFlag:` is a type error rather than a gate that silently fails
|
|
117
|
+
open.
|
|
118
|
+
|
|
119
|
+
A flag answers two questions that are not the same question. `capable` comes
|
|
120
|
+
from the session's scopes and is per-user, advisory, and protects nothing — it
|
|
121
|
+
hides UI. `available` comes from one global config snapshot, is caller-blind,
|
|
122
|
+
and is the only half the runner enforces: `override(subject) ?? (enabled AND
|
|
123
|
+
bucket)`, so "off for everyone except these three organizations" is one row, and
|
|
124
|
+
a kill is one write rather than a fan-out. Authorization stays where it was, in
|
|
125
|
+
the function's own `scopes:` — enforcing capability would make a flag a second
|
|
126
|
+
authorization path OR-ing against them. An unavailable feature throws 503, not
|
|
127
|
+
403, because the caller was allowed; the feature was not on.
|
|
128
|
+
|
|
129
|
+
`featureFlag:` is deliberately available on sessionless functions too. It reads
|
|
130
|
+
the config, not the session, so the kill switch reaches a cron task, a queue
|
|
131
|
+
worker and a webhook — which is where it matters most, since nobody is watching
|
|
132
|
+
a UI to notice the feature is off.
|
|
133
|
+
|
|
134
|
+
Refresh is pull-on-demand, so the TTL is the kill-switch latency. Where that is
|
|
135
|
+
too long, `invalidate()` is public: a provider's change webhook lands on an HTTP
|
|
136
|
+
wiring, drops the cache, and the next request does the read. It deliberately
|
|
137
|
+
does not fetch — a burst of webhooks would be N round trips, and on a serverless
|
|
138
|
+
runtime the isolate that took the signal may be gone before anything reads the
|
|
139
|
+
result.
|
|
140
|
+
|
|
141
|
+
Availability fails open through three layers: a fresh read, then the last good
|
|
142
|
+
cached read, then the compiled declaration. The middle layer is load-bearing —
|
|
143
|
+
dropping straight to the compiled fallback on a blip would switch on every flag
|
|
144
|
+
that was deliberately dark.
|
|
145
|
+
|
|
146
|
+
Backing stores split along what they can honestly do. `FeatureFlagSource` is
|
|
147
|
+
read-only (`snapshot()`) and is what a third-party provider implements;
|
|
148
|
+
`FeatureFlagStore` adds the write half and is for stores Pikku owns.
|
|
149
|
+
`@pikku/kysely` ships the store, with declarations synced additively — a removed
|
|
150
|
+
declaration is marked undeclared, never revoked, and a sync never re-enables a
|
|
151
|
+
killed flag. Third-party providers implement the read-only half in the addons
|
|
152
|
+
repository, over plain `fetch` rather than a vendor SDK — those poll on a timer
|
|
153
|
+
belonging to a long-lived process, which a serverless isolate cannot hold
|
|
154
|
+
between requests.
|
|
155
|
+
|
|
156
|
+
A client asks for its flags once per session rather than per flag:
|
|
157
|
+
`scaffold.featureFlags` generates a `GET /feature-flags` returning every
|
|
158
|
+
declared flag resolved for the caller, keyed by this app's `FeatureFlagName`.
|
|
159
|
+
Generated into the app rather than shipped in an addon precisely for that union
|
|
160
|
+
— an addon never sees the host's, and could only answer
|
|
161
|
+
`Record<string, boolean>`. It returns `show` alone: sending `available` apart
|
|
162
|
+
from `capable` would tell every visitor which features exist but are dark.
|
|
163
|
+
|
|
164
|
+
The write half is the operator's, and lives in `@pikku/addon-admin` beside the
|
|
165
|
+
scope RPCs, under a new `admin:flags` scope. `flagList` reports `writable:
|
|
166
|
+
false` rather than failing when flags come from a provider, because a provider's
|
|
167
|
+
own UI is its operator surface and a console full of buttons that 500 is worse
|
|
168
|
+
than a read-only tab. It still lists the flags: a source may report its declared
|
|
169
|
+
set through `declaredFlags()`, and each row carries `backed`, false where the
|
|
170
|
+
provider has never heard of a declared flag. That row is the one worth seeing —
|
|
171
|
+
an absent row fails open, so a dark launch nobody created in PostHog is already
|
|
172
|
+
live for everyone, and a store pikku owns can reconcile that on deploy where a
|
|
173
|
+
provider cannot.
|
|
174
|
+
|
|
175
|
+
On the client, `createFeatureFlags` fetches that map once and `useFeatureFlag`
|
|
176
|
+
reads it synchronously after, through the same provider the analytics client
|
|
177
|
+
hangs off. It takes a `bootstrap` map so a server-rendered page hydrates onto
|
|
178
|
+
the answer it already computed: without one there is a gap in which neither
|
|
179
|
+
default is right, since false hides a feature the user has and true flashes one
|
|
180
|
+
they do not. A failed refresh keeps the map already on screen rather than
|
|
181
|
+
relabelling every flag on a blip.
|
|
182
|
+
|
|
183
|
+
- Updated dependencies [1c9a55a]
|
|
184
|
+
- Updated dependencies [1c9a55a]
|
|
185
|
+
- Updated dependencies [1c9a55a]
|
|
186
|
+
- @pikku/core@0.12.110
|
|
187
|
+
|
|
188
|
+
## 0.12.79
|
|
189
|
+
|
|
190
|
+
### Patch Changes
|
|
191
|
+
|
|
192
|
+
- 54138b1: Product analytics moves into core behind a `scaffold.analytics` generator.
|
|
193
|
+
|
|
194
|
+
An app declares what it measures, and nothing else:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { defineAnalyticsEvents } from '#pikku/analytics'
|
|
198
|
+
|
|
199
|
+
export const analyticsEvents = defineAnalyticsEvents({
|
|
200
|
+
page_viewed: z.object({ path: z.string() }),
|
|
201
|
+
todo_created: z.object({ priority: z.enum(['low', 'medium', 'high']) }),
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The definer reaches an app through the generated `#pikku/analytics` leaf, the
|
|
206
|
+
way `defineFeatureFlags` reaches it through `#pikku/scopes` — an app declares
|
|
207
|
+
what it measures without importing out of core. The leaf is written on every
|
|
208
|
+
run, whatever `scaffold.analytics` says: the declaration is how a project names
|
|
209
|
+
what it measures, and the scaffold only decides whether an ingest wire is
|
|
210
|
+
generated for it. Its path is `analyticsTypesFile`, defaulting to
|
|
211
|
+
`<outDir>/analytics/pikku-analytics-types.gen.ts`. It carries the sink surface
|
|
212
|
+
as well as the definer — `AnalyticsService`, `AnalyticsRecord`,
|
|
213
|
+
`AnalyticsIdentity`, `AnalyticsClientContext`, `AnalyticsLog`,
|
|
214
|
+
`LoggerAnalyticsService` — because writing the destination events go to is the
|
|
215
|
+
other half of the same job, and both halves should come through one door.
|
|
216
|
+
|
|
217
|
+
The name is the key, so it is never repeated as a `z.literal` inside the
|
|
218
|
+
schema. Declare in as many modules as suits the project — a feature declares
|
|
219
|
+
its events beside its functions — and the CLI unions them into the generated
|
|
220
|
+
`/analytics` ingest and its schemas. The inspector finds the declarations the
|
|
221
|
+
way it finds every other wiring, so there is no path to configure.
|
|
222
|
+
|
|
223
|
+
Where events go is not part of the declaration. `services.analytics` is the
|
|
224
|
+
request-scoped buffer a function records into, typed against the declared
|
|
225
|
+
names:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
await analytics.record({ name: 'todo_created', priority: 'high' })
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
It stamps identity, trace and wire fields from the invocation, buffers for the
|
|
232
|
+
length of the call and flushes once when it ends. Identity is always
|
|
233
|
+
server-side and never read from a request body, which is what makes the
|
|
234
|
+
unauthenticated ingest safe to expose.
|
|
235
|
+
|
|
236
|
+
The transport behind it is an `AnalyticsService` on singleton services — the
|
|
237
|
+
one swappable slot. With none wired the runner installs
|
|
238
|
+
`LoggerAnalyticsService`, so an app that turns the scaffold on can watch events
|
|
239
|
+
arrive without first choosing a store; a platform injects its own through
|
|
240
|
+
singleton services, and an app that wants neither sets `analyticsService` in
|
|
241
|
+
its own `services.ts`, which runs last and wins. There is no process-global
|
|
242
|
+
sink registry and no noop: events are never silently dropped.
|
|
243
|
+
|
|
244
|
+
The generated wire is added to the set of scaffolds the inspector reads.
|
|
245
|
+
Nothing imports it — it is a wiring, not a module anyone calls — so without
|
|
246
|
+
that the route was written and never registered: no HTTP wiring, no entry in
|
|
247
|
+
the fetch client, an endpoint that 404s.
|
|
248
|
+
|
|
249
|
+
No middleware is emitted with the wire: an origin lock rejects every native
|
|
250
|
+
client and is forgeable anyway, so guarding the route is the project's call.
|
|
251
|
+
|
|
252
|
+
`name` belongs to the event, not to its props. The generated union rebuilds
|
|
253
|
+
each member from the declared schema's shape and attaches the literal name
|
|
254
|
+
last, so a declaration that happens to carry a `name` prop of its own cannot
|
|
255
|
+
displace the discriminator the union is built on. Props schemas are typed to
|
|
256
|
+
require that shape, so a schema that has none — a union, a primitive — is
|
|
257
|
+
refused at the declaration rather than in generated code.
|
|
258
|
+
|
|
259
|
+
- Updated dependencies [54138b1]
|
|
260
|
+
- @pikku/core@0.12.109
|
|
261
|
+
|
|
262
|
+
## 0.12.78
|
|
263
|
+
|
|
264
|
+
### Patch Changes
|
|
265
|
+
|
|
266
|
+
- d067ac9: Skip the schema-generator retention assertion under `--coverage`. Coverage
|
|
267
|
+
instrumentation keeps per-line counters alive for the life of the run, so the
|
|
268
|
+
heap delta cannot discriminate a retained `ts.Program` from accumulated
|
|
269
|
+
counters, and `Unit Coverage` failed intermittently while `Unit Tests` passed on
|
|
270
|
+
the same commit. Matches the existing guard in `module-runner.test.ts`.
|
|
271
|
+
|
|
272
|
+
## 0.12.77
|
|
273
|
+
|
|
274
|
+
### Patch Changes
|
|
275
|
+
|
|
276
|
+
- 5c236ee: A deployment unit records why it is where it is. `DeploymentUnit` gains `groupedBy` — the `deploy.grouping` rule that put its functions together — and `targetForcedBy` — the `serverlessIncompatible` services that crossed it to `target: 'server'`. Both are absent for the fallback and for a target that was chosen rather than forced, so anything reading a manifest can tell a deliberate `server` unit from a crossed one. `resolveDeployTarget` now shares `incompatibleServicesFor` with the analyzer, so resolving a target and explaining it cannot drift.
|
|
277
|
+
|
|
278
|
+
## 0.12.76
|
|
279
|
+
|
|
280
|
+
### Patch Changes
|
|
281
|
+
|
|
282
|
+
- 4c7a1b5: Run the monorepo's own scripts through bun instead of yarn. What moves is the
|
|
283
|
+
package manager each package's `prepublishOnly` and build scripts invoke, plus
|
|
284
|
+
the two manifest fixes bun needs to resolve the tree: `uWebSockets.js` is
|
|
285
|
+
declared with an explicit `github:` specifier, and `@pikku/uws-handler` marks
|
|
286
|
+
its `uWebSockets.js` peer optional so a bun install of a consumer that brings
|
|
287
|
+
its own uWS app does not try to fetch it from the registry.
|
|
288
|
+
|
|
289
|
+
Three published behaviours change, all of them cases where an isolated
|
|
290
|
+
`node_modules` or bun as the runtime had been papered over by yarn's hoisting:
|
|
291
|
+
|
|
292
|
+
- `@pikku/migrator-sql` turns foreign keys on when it opens a sqlite database
|
|
293
|
+
through bun. `node:sqlite` enforces them by default and `bun:sqlite` does not,
|
|
294
|
+
which silently turned every `ON DELETE CASCADE` into a no-op under
|
|
295
|
+
`bunx --bun pikku`.
|
|
296
|
+
- `@pikku/cli` resolves a deploy provider against the project being deployed
|
|
297
|
+
rather than against wherever the CLI itself is installed, which is what its
|
|
298
|
+
own "is not installed" error asks the user to arrange.
|
|
299
|
+
- `@pikku/cli` treats a specifier a runtime hands straight back — bun does this
|
|
300
|
+
for the modules it implements itself — as not resolved from the project, so
|
|
301
|
+
it falls back rather than loading the runtime's own copy.
|
|
302
|
+
|
|
303
|
+
- 9d7a96d: Deprioritise the generated session middleware instead of skipping it
|
|
304
|
+
|
|
305
|
+
Two session middlewares were reconciled by having the CLI inspect the app for
|
|
306
|
+
its own `betterAuthSession` / `betterAuthStatelessSession` registration and then
|
|
307
|
+
generate nothing (pikkujs/pikku#754). That silently dropped everything emitted
|
|
308
|
+
alongside — including the `PIKKU_CONSOLE_TOKEN` bearer, so an app that
|
|
309
|
+
customized `mapSession` answered `MissingScopeError` on every `console:*` RPC.
|
|
310
|
+
|
|
311
|
+
All three session middlewares now take a `priority`, and the CLI generates its
|
|
312
|
+
registration at `lowest`. An app's own registration defaults to `medium`, so it
|
|
313
|
+
resolves the session first and the generated one short-circuits on its existing
|
|
314
|
+
`if (session) next()`. Nothing is detected and nothing is skipped;
|
|
315
|
+
`state.auth.userStatelessSession` and `state.auth.hasUserSessionMiddleware` are
|
|
316
|
+
gone.
|
|
317
|
+
|
|
318
|
+
`betterAuthSession`, `betterAuthStatelessSession` and the session store now also
|
|
319
|
+
carry the organization plugin's `activeOrganizationId` onto `session.orgId`
|
|
320
|
+
without a `mapSession`, and `impersonation.loadUser` defaults to better-auth's
|
|
321
|
+
own `findUserById` — the two things an app most often wrote a whole `mapSession`
|
|
322
|
+
for.
|
|
323
|
+
|
|
324
|
+
- bb6abc2: The CLI's own code no longer imports the TypeScript compiler API. `collectSurface` moved out of `@pikku/cli` into a new `@pikku/inspector/surface` entry point, and the two smaller compiler needs the CLI had are exposed as `readModuleSpecifiers` and `readTsconfigOutDir`.
|
|
325
|
+
|
|
326
|
+
`typescript` drops to a devDependency on `@pikku/cli`. It still arrives transitively through `@pikku/code-edit`, which needs a parser to rewrite your source and is loaded only by `pikku meta apply` and the console's edit RPCs.
|
|
327
|
+
|
|
328
|
+
- Updated dependencies [4c7a1b5]
|
|
329
|
+
- @pikku/core@0.12.108
|
|
330
|
+
|
|
331
|
+
## 0.12.75
|
|
332
|
+
|
|
333
|
+
### Patch Changes
|
|
334
|
+
|
|
335
|
+
- b28a8c0: An addon's exposed functions get their own deployment unit
|
|
336
|
+
|
|
337
|
+
`collectFilterNames` adds the `/rpc/:rpcName` scaffold name to every unit holding an exposed function, so that unit can serve its own `/rpc/<funcName>`. `filterInspectorState` read that same string back as "this unit is the generic dispatcher" and kept every wired addon's declaration for it, so almost every function unit imported each addon's whole package bootstrap — and the node-only modules it reaches.
|
|
338
|
+
|
|
339
|
+
No unit retains a wired addon on the strength of a catch-all route any more. Instead the analyzer emits a unit per addon carrying that addon's exposed functions, and the unit serving `/rpc/:rpcName` dispatches to it over a service binding rather than bundling it. Granularity is per addon rather than per function because an addon publishes a single prebuilt bootstrap that registers all of its functions at once.
|
|
340
|
+
|
|
341
|
+
`node:child_process` joins `node:fs` as a stubbed builtin: Workers have no implementation of it under any compatibility flag, and it is reached from the console addon's audit tooling.
|
|
342
|
+
|
|
343
|
+
`DeploymentUnit` gains `dispatch`, an RPC-name → unit-name map for calls whose target unit isn't the kebab-cased RPC name. `rpc.exposed(name)` falls through to the deployment service for a namespaced name with no local metadata, and Cloudflare function units with a `dependsOn` now get a `deploymentService`, which previously only gateway units had.
|
|
344
|
+
|
|
345
|
+
- Updated dependencies [b28a8c0]
|
|
346
|
+
- @pikku/core@0.12.107
|
|
347
|
+
|
|
1
348
|
## 0.12.74
|
|
2
349
|
|
|
3
350
|
### Patch Changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import type { InspectorLogger, InspectorState } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Record where each `defineAnalyticsEvents` declaration is, which names it
|
|
5
|
+
* declares, and the shape each name validates — the names drive the generated
|
|
6
|
+
* ingest, which reads the schemas off the declaration itself; the shapes are
|
|
7
|
+
* read here only so the console can show what an event carries.
|
|
8
|
+
*/
|
|
9
|
+
export declare const addAnalytics: (logger: InspectorLogger, node: ts.Node, checker: ts.TypeChecker, state: InspectorState) => void;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { isNamedExport } from '../utils/extract-function-name.js';
|
|
3
|
+
const DEFINE_ANALYTICS_EVENTS = 'defineAnalyticsEvents';
|
|
4
|
+
/**
|
|
5
|
+
* A call is the pikku one when the symbol it resolves to is, whatever the file
|
|
6
|
+
* chose to call it locally — `import { defineAnalyticsEvents as declare }` is
|
|
7
|
+
* an ordinary thing to write, and matching on the callee's text alone both
|
|
8
|
+
* misses that and claims a same-named local helper.
|
|
9
|
+
*/
|
|
10
|
+
const callsDefineAnalyticsEvents = (expression, checker) => {
|
|
11
|
+
if (!ts.isIdentifier(expression))
|
|
12
|
+
return false;
|
|
13
|
+
const symbol = checker.getSymbolAtLocation(expression);
|
|
14
|
+
if (!symbol)
|
|
15
|
+
return expression.text === DEFINE_ANALYTICS_EVENTS;
|
|
16
|
+
// An import specifier names what it imported even when the module itself
|
|
17
|
+
// does not resolve, which is every project whose deps are not installed yet.
|
|
18
|
+
const declaration = symbol.declarations?.[0];
|
|
19
|
+
if (declaration && ts.isImportSpecifier(declaration)) {
|
|
20
|
+
return ((declaration.propertyName ?? declaration.name).text ===
|
|
21
|
+
DEFINE_ANALYTICS_EVENTS);
|
|
22
|
+
}
|
|
23
|
+
// Only an import can be pikku's. A file's own helper of the same name
|
|
24
|
+
// resolves to a symbol whose `.name` matches, so resolving without this
|
|
25
|
+
// claims it — the mirror of what matching on the callee's text misses.
|
|
26
|
+
if (!(symbol.flags & ts.SymbolFlags.Alias))
|
|
27
|
+
return false;
|
|
28
|
+
const resolved = checker.getAliasedSymbol(symbol) ?? symbol;
|
|
29
|
+
return resolved.name === DEFINE_ANALYTICS_EVENTS;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The name the generated ingest has to import the declaration by — which is
|
|
33
|
+
* the name the module exports it under, not the name of the const. A module
|
|
34
|
+
* that keeps the declaration to itself has no such name, so it is refused here
|
|
35
|
+
* rather than emitted into an ingest that cannot compile.
|
|
36
|
+
*/
|
|
37
|
+
const exportedName = (declaration, name, checker) => {
|
|
38
|
+
const moduleSymbol = checker.getSymbolAtLocation(declaration.getSourceFile());
|
|
39
|
+
const declarationSymbol = checker.getSymbolAtLocation(name);
|
|
40
|
+
if (!moduleSymbol || !declarationSymbol) {
|
|
41
|
+
return isNamedExport(declaration, checker) ? name.text : undefined;
|
|
42
|
+
}
|
|
43
|
+
for (const exported of checker.getExportsOfModule(moduleSymbol)) {
|
|
44
|
+
const resolved = exported.flags & ts.SymbolFlags.Alias
|
|
45
|
+
? (checker.getAliasedSymbol(exported) ?? exported)
|
|
46
|
+
: exported;
|
|
47
|
+
if (resolved === declarationSymbol)
|
|
48
|
+
return exported.name;
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The `z.object({ ... })` an event's schema is built from, wherever it sits in
|
|
54
|
+
* a chain — `z.object({}).strict()` and `z.object({}).describe('…')` are both
|
|
55
|
+
* ordinary things to write, and the shape is on the innermost call either way.
|
|
56
|
+
*/
|
|
57
|
+
const findObjectShape = (expression) => {
|
|
58
|
+
let current = expression;
|
|
59
|
+
while (current && ts.isCallExpression(current)) {
|
|
60
|
+
const callee = current.expression;
|
|
61
|
+
if (ts.isPropertyAccessExpression(callee) &&
|
|
62
|
+
callee.name.text === 'object') {
|
|
63
|
+
const [shape] = current.arguments;
|
|
64
|
+
if (shape && ts.isObjectLiteralExpression(shape))
|
|
65
|
+
return shape;
|
|
66
|
+
}
|
|
67
|
+
current = ts.isPropertyAccessExpression(callee)
|
|
68
|
+
? callee.expression
|
|
69
|
+
: undefined;
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* An event's props, as source text rather than a resolved type — `z.string()`
|
|
75
|
+
* is what the declaration says and what a reader recognises, and resolving it
|
|
76
|
+
* would mean type-checking a third-party generic to render one table cell.
|
|
77
|
+
*
|
|
78
|
+
* Absent for a schema pikku cannot read the shape off (a shared const, a union,
|
|
79
|
+
* a vendor that is not zod). The catalog renders nothing rather than guessing.
|
|
80
|
+
*/
|
|
81
|
+
const readEventShape = (initializer) => {
|
|
82
|
+
const shape = findObjectShape(initializer);
|
|
83
|
+
if (!shape)
|
|
84
|
+
return undefined;
|
|
85
|
+
const props = {};
|
|
86
|
+
for (const property of shape.properties) {
|
|
87
|
+
if (!ts.isPropertyAssignment(property))
|
|
88
|
+
continue;
|
|
89
|
+
const key = property.name;
|
|
90
|
+
const propName = ts.isIdentifier(key)
|
|
91
|
+
? key.text
|
|
92
|
+
: ts.isStringLiteral(key)
|
|
93
|
+
? key.text
|
|
94
|
+
: undefined;
|
|
95
|
+
if (propName === undefined)
|
|
96
|
+
continue;
|
|
97
|
+
props[propName] = property.initializer.getText().replace(/\s+/g, ' ');
|
|
98
|
+
}
|
|
99
|
+
return props;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Record where each `defineAnalyticsEvents` declaration is, which names it
|
|
103
|
+
* declares, and the shape each name validates — the names drive the generated
|
|
104
|
+
* ingest, which reads the schemas off the declaration itself; the shapes are
|
|
105
|
+
* read here only so the console can show what an event carries.
|
|
106
|
+
*/
|
|
107
|
+
export const addAnalytics = (logger, node, checker, state) => {
|
|
108
|
+
if (!ts.isVariableDeclaration(node))
|
|
109
|
+
return;
|
|
110
|
+
const { initializer, name } = node;
|
|
111
|
+
if (!initializer || !ts.isCallExpression(initializer))
|
|
112
|
+
return;
|
|
113
|
+
if (!callsDefineAnalyticsEvents(initializer.expression, checker))
|
|
114
|
+
return;
|
|
115
|
+
if (!ts.isIdentifier(name))
|
|
116
|
+
return;
|
|
117
|
+
const file = node.getSourceFile().fileName;
|
|
118
|
+
const [argument] = initializer.arguments;
|
|
119
|
+
if (!argument || !ts.isObjectLiteralExpression(argument)) {
|
|
120
|
+
logger.error(`defineAnalyticsEvents in ${file} must be called with an object literal keyed by event name, so the generated ingest knows what to union.`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const events = [];
|
|
124
|
+
const props = {};
|
|
125
|
+
for (const property of argument.properties) {
|
|
126
|
+
// `{ page_viewed }` names the event in the shorthand's own identifier.
|
|
127
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
128
|
+
events.push(property.name.text);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!ts.isPropertyAssignment(property))
|
|
132
|
+
continue;
|
|
133
|
+
const key = property.name;
|
|
134
|
+
const eventName = ts.isIdentifier(key)
|
|
135
|
+
? key.text
|
|
136
|
+
: ts.isStringLiteral(key)
|
|
137
|
+
? key.text
|
|
138
|
+
: undefined;
|
|
139
|
+
if (eventName === undefined)
|
|
140
|
+
continue;
|
|
141
|
+
events.push(eventName);
|
|
142
|
+
const shape = readEventShape(property.initializer);
|
|
143
|
+
if (shape)
|
|
144
|
+
props[eventName] = shape;
|
|
145
|
+
}
|
|
146
|
+
if (events.length === 0) {
|
|
147
|
+
logger.error(`defineAnalyticsEvents in ${file} declares no events. Remove it or add one.`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const variable = exportedName(node, name, checker);
|
|
151
|
+
if (!variable) {
|
|
152
|
+
logger.error(`defineAnalyticsEvents in ${file} is assigned to '${name.text}', which the module does not export. The generated ingest imports every declaration by name, so export it.`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// A declaration whose shapes the inspector could not read carries no props,
|
|
156
|
+
// which the meta says by leaving the field off rather than by an empty object.
|
|
157
|
+
const readProps = Object.keys(props).length > 0 ? props : undefined;
|
|
158
|
+
const declarations = (state.analytics ??= []);
|
|
159
|
+
const existing = declarations.find((declaration) => declaration.file === file && declaration.variable === variable);
|
|
160
|
+
if (existing) {
|
|
161
|
+
existing.events = events;
|
|
162
|
+
if (readProps)
|
|
163
|
+
existing.props = readProps;
|
|
164
|
+
else
|
|
165
|
+
delete existing.props;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
for (const eventName of events) {
|
|
169
|
+
const declaredIn = declarations.find((declaration) => declaration.events.includes(eventName));
|
|
170
|
+
if (declaredIn) {
|
|
171
|
+
logger.error(`Analytics event '${eventName}' is declared twice: ${declaredIn.file} and ${file}. An event name belongs to one declaration.`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
declarations.push(readProps
|
|
176
|
+
? { file, variable, events, props: readProps }
|
|
177
|
+
: { file, variable, events });
|
|
178
|
+
};
|
package/dist/add/add-auth.js
CHANGED
|
@@ -108,36 +108,6 @@ const readPluginEntry = (el, bindings) => {
|
|
|
108
108
|
}
|
|
109
109
|
return undefined;
|
|
110
110
|
};
|
|
111
|
-
/**
|
|
112
|
-
* True when `node` sits inside a GLOBAL middleware registration — i.e. an actual
|
|
113
|
-
* global registration, not a bare standalone call or a route-scoped one.
|
|
114
|
-
*
|
|
115
|
-
* `addGlobalMiddleware(...)` is always global. `addHTTPMiddleware` is global only
|
|
116
|
-
* in its array form (`addHTTPMiddleware([...])`) or with the `'*'` wildcard
|
|
117
|
-
* pattern; a specific route pattern (`addHTTPMiddleware('/api/admin/*', [...])`)
|
|
118
|
-
* scopes the middleware to that route and must NOT count as a global stateless
|
|
119
|
-
* registration (#754).
|
|
120
|
-
*/
|
|
121
|
-
const isInsideGlobalMiddlewareRegistration = (node) => {
|
|
122
|
-
let parent = node.parent;
|
|
123
|
-
while (parent) {
|
|
124
|
-
if (ts.isCallExpression(parent) && ts.isIdentifier(parent.expression)) {
|
|
125
|
-
const fn = parent.expression.text;
|
|
126
|
-
if (fn === 'addGlobalMiddleware')
|
|
127
|
-
return true;
|
|
128
|
-
if (fn === 'addHTTPMiddleware') {
|
|
129
|
-
const first = parent.arguments[0];
|
|
130
|
-
if (!first)
|
|
131
|
-
return false;
|
|
132
|
-
// String first arg → route pattern (global only when '*'); otherwise
|
|
133
|
-
// the array form, which is global.
|
|
134
|
-
return ts.isStringLiteral(first) ? first.text === '*' : true;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
parent = parent.parent;
|
|
138
|
-
}
|
|
139
|
-
return false;
|
|
140
|
-
};
|
|
141
111
|
/**
|
|
142
112
|
* Refusal for better-auth's `admin()`, which pikku does not support.
|
|
143
113
|
*
|
|
@@ -193,30 +163,6 @@ export const addAuth = (logger, node, _checker, state) => {
|
|
|
193
163
|
if (!ts.isCallExpression(node))
|
|
194
164
|
return;
|
|
195
165
|
const expression = node.expression;
|
|
196
|
-
// A user-registered stateless session middleware (custom mapSession) means the
|
|
197
|
-
// CLI must NOT auto-generate its own default-map one — the generated one runs
|
|
198
|
-
// first and pre-empts the user's via the `if (session) next()` short-circuit
|
|
199
|
-
// (pikkujs/pikku#754). Only a GLOBAL registration counts (inside
|
|
200
|
-
// addHTTPMiddleware/addGlobalMiddleware) — a bare betterAuthStatelessSession()
|
|
201
|
-
// call (e.g. a test harness) is not a registration. Ignore generated files so
|
|
202
|
-
// the emitted middleware can't self-trigger the skip.
|
|
203
|
-
if (ts.isIdentifier(expression) &&
|
|
204
|
-
expression.text === 'betterAuthStatelessSession' &&
|
|
205
|
-
!node.getSourceFile().fileName.endsWith('.gen.ts') &&
|
|
206
|
-
isInsideGlobalMiddlewareRegistration(node)) {
|
|
207
|
-
state.auth.userStatelessSession = true;
|
|
208
|
-
}
|
|
209
|
-
// Same rule for the stateful variant: a user-registered global
|
|
210
|
-
// betterAuthSession(...) (custom mapSession/impersonation/apiKey) means the CLI
|
|
211
|
-
// must NOT auto-generate its own default one — the generated one runs first and
|
|
212
|
-
// pre-empts the user's via the `if (session) next()` short-circuit. Stateful
|
|
213
|
-
// analogue of the betterAuthStatelessSession skip above.
|
|
214
|
-
if (ts.isIdentifier(expression) &&
|
|
215
|
-
expression.text === 'betterAuthSession' &&
|
|
216
|
-
!node.getSourceFile().fileName.endsWith('.gen.ts') &&
|
|
217
|
-
isInsideGlobalMiddlewareRegistration(node)) {
|
|
218
|
-
state.auth.hasUserSessionMiddleware = true;
|
|
219
|
-
}
|
|
220
166
|
if (!ts.isIdentifier(expression) || expression.text !== 'pikkuBetterAuth')
|
|
221
167
|
return;
|
|
222
168
|
const sourceFile = node.getSourceFile().fileName;
|