@pikku/core 0.12.128 → 0.12.129
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 +6 -0
- package/dist/function/function-runner.d.ts +6 -1
- package/dist/function/function-runner.js +18 -14
- package/dist/wirings/channel/channel-common.d.ts +3 -1
- package/dist/wirings/channel/channel-common.js +2 -1
- package/dist/wirings/channel/channel-handler.d.ts +1 -1
- package/dist/wirings/channel/channel-handler.js +2 -1
- package/dist/wirings/channel/local/local-channel-runner.js +3 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -1
- package/knowledge/decisions/internals/index.md +1 -0
- package/knowledge/decisions/internals/the-runner-resolves-wire-services-itself.md +31 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## 0.12.129
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 1ab6eb4: Fix app functions reached through `rpc.invoke`/`rpc.exposed` (including the generated `/rpc/:rpcName` endpoint) running without their wire services. Since `singletonServicesOnly`, the forwarding `rpcCaller` built none and the callee inherited that, so any service from `pikkuWireServices` was `undefined`. The runner now resolves the callee's factory itself. Channels pass their per-connection set as the new `wireServices` option, which is reused and left open.
|
|
6
|
+
|
|
1
7
|
## 0.12.128
|
|
2
8
|
|
|
3
9
|
### Patch Changes
|
|
@@ -8,9 +8,14 @@ import type { AddonInstance } from '../wirings/addon/addon-runner.js';
|
|
|
8
8
|
export declare const addFunction: (funcName: string, funcConfig: CorePikkuFunctionConfig<any, any, any, any, any>, packageName?: string | null) => void;
|
|
9
9
|
export declare const getFunctionNames: (packageName?: string | null) => string[];
|
|
10
10
|
export declare const getAllFunctionNames: () => string[];
|
|
11
|
-
export declare const runPikkuFunc: <In = any, Out = any>(wireType: PikkuWiringTypes, wireId: string, funcName: string, { singletonServices, createWireServices, data, auth: wiringAuth, inheritedMiddleware, wireMiddleware, inheritedChannelMiddleware, wireChannelMiddleware, coerceDataFromSchema, wire, sessionService, credentialWireService, packageName, addonInstance, }: {
|
|
11
|
+
export declare const runPikkuFunc: <In = any, Out = any>(wireType: PikkuWiringTypes, wireId: string, funcName: string, { singletonServices, createWireServices, wireServices: providedWireServices, data, auth: wiringAuth, inheritedMiddleware, wireMiddleware, inheritedChannelMiddleware, wireChannelMiddleware, coerceDataFromSchema, wire, sessionService, credentialWireService, packageName, addonInstance, }: {
|
|
12
12
|
singletonServices: CoreSingletonServices;
|
|
13
13
|
createWireServices?: CreateWireServices;
|
|
14
|
+
/**
|
|
15
|
+
* Wire services the caller already built and still owns, such as a
|
|
16
|
+
* channel's per-connection set. Used as-is and never closed here.
|
|
17
|
+
*/
|
|
18
|
+
wireServices?: Record<string, unknown>;
|
|
14
19
|
data: () => Promise<In> | In;
|
|
15
20
|
auth?: boolean;
|
|
16
21
|
inheritedMiddleware?: MiddlewareMetadata[];
|
|
@@ -3,7 +3,7 @@ import { runMiddleware, combineMiddleware } from '../middleware-runner.js';
|
|
|
3
3
|
import { combineChannelMiddleware, wrapChannelWithMiddleware, } from '../wirings/channel/channel-middleware-runner.js';
|
|
4
4
|
import { runPermissions } from '../permissions.js';
|
|
5
5
|
import { withoutSecrets } from '../services/secretless.js';
|
|
6
|
-
import { pikkuState } from '../pikku-state.js';
|
|
6
|
+
import { getCreateWireServices, pikkuState } from '../pikku-state.js';
|
|
7
7
|
import { applyDefaultsFromSchema, coerceTopLevelDataFromSchema, validateSchema, } from '../schema.js';
|
|
8
8
|
import { parseVersionedId } from '../version.js';
|
|
9
9
|
import { PikkuSessionService } from '../services/user-session-service.js';
|
|
@@ -64,7 +64,7 @@ export const getAllFunctionNames = () => {
|
|
|
64
64
|
}
|
|
65
65
|
return functions;
|
|
66
66
|
};
|
|
67
|
-
export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServices, createWireServices, data, auth: wiringAuth, inheritedMiddleware, wireMiddleware, inheritedChannelMiddleware, wireChannelMiddleware, coerceDataFromSchema, wire, sessionService, credentialWireService, packageName = null, addonInstance, }) => {
|
|
67
|
+
export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServices, createWireServices, wireServices: providedWireServices, data, auth: wiringAuth, inheritedMiddleware, wireMiddleware, inheritedChannelMiddleware, wireChannelMiddleware, coerceDataFromSchema, wire, sessionService, credentialWireService, packageName = null, addonInstance, }) => {
|
|
68
68
|
wire.wireType ??= wireType;
|
|
69
69
|
wire.wireId ??= wireId;
|
|
70
70
|
const funcMap = pikkuState(packageName, 'function', 'functions');
|
|
@@ -118,13 +118,14 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServic
|
|
|
118
118
|
const resolvedSingletonServices = funcPackageName
|
|
119
119
|
? await getOrCreatePackageSingletonServices(funcPackageName, singletonServices, addonInstance)
|
|
120
120
|
: singletonServices;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
// knowledge: decisions/internals/the-runner-resolves-wire-services-itself.md
|
|
122
|
+
const packageCreateWireServices = funcPackageName
|
|
123
|
+
? pikkuState(funcPackageName, 'package', 'factories')?.createWireServices
|
|
124
|
+
: undefined;
|
|
125
|
+
const reusedWireServices = packageCreateWireServices
|
|
126
|
+
? undefined
|
|
127
|
+
: providedWireServices;
|
|
128
|
+
const resolvedCreateWireServices = packageCreateWireServices ?? createWireServices ?? getCreateWireServices();
|
|
128
129
|
const allChannelMiddleware = combineChannelMiddleware(wireType, wireId, {
|
|
129
130
|
wireInheritedChannelMiddleware: inheritedChannelMiddleware,
|
|
130
131
|
wireChannelMiddleware,
|
|
@@ -256,11 +257,14 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServic
|
|
|
256
257
|
let invocationAuditLog;
|
|
257
258
|
let invocationAnalytics;
|
|
258
259
|
try {
|
|
259
|
-
|
|
260
|
+
if (!funcMeta.singletonServicesOnly && !reusedWireServices) {
|
|
261
|
+
wireServices = (await resolvedCreateWireServices?.(resolvedSingletonServices, invocationWire));
|
|
262
|
+
}
|
|
263
|
+
const invocationWireServices = funcMeta.singletonServicesOnly
|
|
260
264
|
? undefined
|
|
261
|
-
: (
|
|
262
|
-
let services =
|
|
263
|
-
? { ...resolvedSingletonServices, ...
|
|
265
|
+
: (reusedWireServices ?? wireServices);
|
|
266
|
+
let services = invocationWireServices && Object.keys(invocationWireServices).length > 0
|
|
267
|
+
? { ...resolvedSingletonServices, ...invocationWireServices }
|
|
264
268
|
: resolvedSingletonServices;
|
|
265
269
|
// knowledge: decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md
|
|
266
270
|
if (resolvedAuditConfig &&
|
|
@@ -286,7 +290,7 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServic
|
|
|
286
290
|
const callerPackageName = funcPackageName;
|
|
287
291
|
Object.defineProperty(invocationWire, 'rpc', {
|
|
288
292
|
get() {
|
|
289
|
-
const rpc = rpcService.getContextRPCService(
|
|
293
|
+
const rpc = rpcService.getContextRPCService(resolvedSingletonServices, invocationWire, { sessionService }, 0, callerPackageName);
|
|
290
294
|
Object.defineProperty(invocationWire, 'rpc', {
|
|
291
295
|
value: rpc,
|
|
292
296
|
writable: true,
|
|
@@ -2,7 +2,7 @@ import type { CoreSingletonServices } from '../../types/core.types.js';
|
|
|
2
2
|
import type { MiddlewareMetadata } from '../../middleware/middleware.types.js';
|
|
3
3
|
import type { CoreChannel, ChannelMessageMeta } from './channel.types.js';
|
|
4
4
|
import { type PikkuSessionService } from '../../services/user-session-service.js';
|
|
5
|
-
export declare const runChannelLifecycleWithMiddleware: ({ channelConfig, meta, lifecycleConfig, lifecycleType, services, channel, data, channelMiddlewareMeta, userSession, }: {
|
|
5
|
+
export declare const runChannelLifecycleWithMiddleware: ({ channelConfig, meta, lifecycleConfig, lifecycleType, services, channel, data, channelMiddlewareMeta, userSession, wireServices, }: {
|
|
6
6
|
channelConfig: CoreChannel<unknown, any>;
|
|
7
7
|
meta: ChannelMessageMeta;
|
|
8
8
|
lifecycleConfig: any;
|
|
@@ -16,4 +16,6 @@ export declare const runChannelLifecycleWithMiddleware: ({ channelConfig, meta,
|
|
|
16
16
|
* just connected.
|
|
17
17
|
*/
|
|
18
18
|
userSession?: PikkuSessionService<any>;
|
|
19
|
+
/** The connection's wire services, reused rather than rebuilt per call. */
|
|
20
|
+
wireServices?: Record<string, unknown>;
|
|
19
21
|
}) => Promise<unknown>;
|
|
@@ -2,7 +2,7 @@ import { combineMiddleware, runMiddleware } from '../../middleware-runner.js';
|
|
|
2
2
|
import { runPikkuFunc } from '../../function/function-runner.js';
|
|
3
3
|
import { createMiddlewareSessionWireProps, } from '../../services/user-session-service.js';
|
|
4
4
|
import { combineChannelMiddleware, wrapChannelWithMiddleware, } from './channel-middleware-runner.js';
|
|
5
|
-
export const runChannelLifecycleWithMiddleware = async ({ channelConfig, meta, lifecycleConfig, lifecycleType, services, channel, data, channelMiddlewareMeta, userSession, }) => {
|
|
5
|
+
export const runChannelLifecycleWithMiddleware = async ({ channelConfig, meta, lifecycleConfig, lifecycleType, services, channel, data, channelMiddlewareMeta, userSession, wireServices, }) => {
|
|
6
6
|
const lifecycleMiddleware = typeof lifecycleConfig === 'object' && 'middleware' in lifecycleConfig
|
|
7
7
|
? lifecycleConfig.middleware || []
|
|
8
8
|
: [];
|
|
@@ -25,6 +25,7 @@ export const runChannelLifecycleWithMiddleware = async ({ channelConfig, meta, l
|
|
|
25
25
|
const runLifecycle = async () => {
|
|
26
26
|
return await runPikkuFunc('channel', channelConfig.name, meta.pikkuFuncId, {
|
|
27
27
|
singletonServices: services,
|
|
28
|
+
wireServices,
|
|
28
29
|
data: () => data,
|
|
29
30
|
wire,
|
|
30
31
|
sessionService: userSession,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CoreServices, CoreUserSession } from '../../types/core.types.js';
|
|
2
2
|
import type { BinaryData, CoreChannel, PikkuChannelHandler } from './channel.types.js';
|
|
3
3
|
import type { SessionService } from '../../services/user-session-service.js';
|
|
4
|
-
export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreChannel<any, any>, channelHandler: PikkuChannelHandler, userSession?: SessionService<CoreUserSession>) => {
|
|
4
|
+
export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreChannel<any, any>, channelHandler: PikkuChannelHandler, userSession?: SessionService<CoreUserSession>, wireServices?: Record<string, unknown>) => {
|
|
5
5
|
onMessage: (rawData: unknown) => Promise<unknown>;
|
|
6
6
|
onBinaryMessage: ((data: BinaryData) => Promise<BinaryData | void>) | undefined;
|
|
7
7
|
};
|
|
@@ -37,7 +37,7 @@ const validateAuth = (requiresSession, session, onMessage) => {
|
|
|
37
37
|
}
|
|
38
38
|
return true;
|
|
39
39
|
};
|
|
40
|
-
export const processMessageHandlers = (services, channelConfig, channelHandler, userSession) => {
|
|
40
|
+
export const processMessageHandlers = (services, channelConfig, channelHandler, userSession, wireServices) => {
|
|
41
41
|
const logger = services.logger;
|
|
42
42
|
const requiresSession = channelConfig.auth !== false;
|
|
43
43
|
const processMessage = async (data, onMessage, routingProperty, routerValue) => {
|
|
@@ -76,6 +76,7 @@ export const processMessageHandlers = (services, channelConfig, channelHandler,
|
|
|
76
76
|
const wireChannelMiddleware = channelConfig.channelMiddleware || [];
|
|
77
77
|
return await runPikkuFunc('channel', cacheKey, pikkuFuncId, {
|
|
78
78
|
singletonServices: services,
|
|
79
|
+
wireServices,
|
|
79
80
|
data: () => data,
|
|
80
81
|
inheritedMiddleware,
|
|
81
82
|
wireMiddleware,
|
|
@@ -89,6 +89,7 @@ export const runLocalChannel = async ({ channelId, request, response, route, ski
|
|
|
89
89
|
data: openingData,
|
|
90
90
|
channelMiddlewareMeta: meta.channelMiddleware,
|
|
91
91
|
userSession,
|
|
92
|
+
wireServices,
|
|
92
93
|
});
|
|
93
94
|
if (result !== undefined) {
|
|
94
95
|
await channel.send(result);
|
|
@@ -117,6 +118,7 @@ export const runLocalChannel = async ({ channelId, request, response, route, ski
|
|
|
117
118
|
channel,
|
|
118
119
|
channelMiddlewareMeta: meta.channelMiddleware,
|
|
119
120
|
userSession,
|
|
121
|
+
wireServices,
|
|
120
122
|
});
|
|
121
123
|
}
|
|
122
124
|
catch (e) {
|
|
@@ -131,7 +133,7 @@ export const runLocalChannel = async ({ channelId, request, response, route, ski
|
|
|
131
133
|
}
|
|
132
134
|
await closeServices();
|
|
133
135
|
});
|
|
134
|
-
const { onMessage, onBinaryMessage } = processMessageHandlers(services, channelConfig, channelHandler, userSession);
|
|
136
|
+
const { onMessage, onBinaryMessage } = processMessageHandlers(services, channelConfig, channelHandler, userSession, wireServices);
|
|
135
137
|
channelHandler.registerOnMessage(async (data) => {
|
|
136
138
|
try {
|
|
137
139
|
const result = await onMessage(data);
|
|
@@ -80,6 +80,7 @@ export const runChannelConnect = async ({ channelId, channelObject, request, res
|
|
|
80
80
|
data: openingData,
|
|
81
81
|
channelMiddlewareMeta: meta.channelMiddleware,
|
|
82
82
|
userSession,
|
|
83
|
+
wireServices,
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
86
|
const pikkuUserId = userSession.getPikkuUserId();
|
|
@@ -150,6 +151,7 @@ export const runChannelDisconnect = async ({ channelId, channelStore, channelHan
|
|
|
150
151
|
channel,
|
|
151
152
|
channelMiddlewareMeta: meta.channelMiddleware,
|
|
152
153
|
userSession,
|
|
154
|
+
wireServices,
|
|
153
155
|
});
|
|
154
156
|
}
|
|
155
157
|
catch (e) {
|
|
@@ -197,7 +199,7 @@ export const runChannelMessage = async ({ channelId, channelStore, channelHandle
|
|
|
197
199
|
};
|
|
198
200
|
let response;
|
|
199
201
|
try {
|
|
200
|
-
const { onMessage } = processMessageHandlers(services, channelConfig, channelHandler, userSession);
|
|
202
|
+
const { onMessage } = processMessageHandlers(services, channelConfig, channelHandler, userSession, wireServices);
|
|
201
203
|
response = await onMessage(data);
|
|
202
204
|
}
|
|
203
205
|
catch (e) {
|
|
@@ -62,6 +62,7 @@ caller is entitled to assume.
|
|
|
62
62
|
- [A column's at-rest form is an axis of its own](core-column-form-is-an-axis-of-its-own.md) — How a value is stored is independent of how sensitive it is, so form carries a required nominal brand on writes while classification stays optional on reads
|
|
63
63
|
- [The data-classification brand is an optional property](core-data-classification-brand-is-an-optional-property.md) — Making **classification** required would break ordinary Kysely operands, so the brand only constrains values flowing out
|
|
64
64
|
- [The function runner restores the wire fields it overwrites](core-function-runner-restores-the-wire-fields-it-overwrites.md) — One wire object is reused across nested calls, so functionId, audit, addonNamespace and rpc are saved and put back in a finally
|
|
65
|
+
- [The function runner resolves wire services itself](the-runner-resolves-wire-services-itself.md) — A function's wire services come from its own package's factory or the app's registered one, never borrowed from whoever called it
|
|
65
66
|
- [Hot reload merges generated meta and never replaces it](core-hot-reload-merges-generated-meta-never-replaces-it.md) — Reloading codegen output must preserve runtime-registered meta, which no generated JSON contains
|
|
66
67
|
- [Hot reload owns its module registry instead of re-importing](core-hot-reload-owns-its-module-registry.md) — Dev reload transpiles to CJS and runs modules through vm.compileFunction, because the native ESM loader map cannot be evicted
|
|
67
68
|
- [Middleware order is resolution scope first, then priority](core-middleware-order-is-scope-then-priority.md) — Middleware is collected global to function, then stably sorted by priority, deduped, frozen and cached per wire
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The function runner resolves wire services itself
|
|
4
|
+
description: A function's wire services come from its own package's factory or the app's registered one, never borrowed from whoever called it
|
|
5
|
+
tags: core
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The function runner resolves wire services itself
|
|
9
|
+
|
|
10
|
+
`runPikkuFunc` picks the wire-services factory for the function it is about to
|
|
11
|
+
run: the function's addon package factory if it has one, else a factory the
|
|
12
|
+
transport passed explicitly, else the app's registered one
|
|
13
|
+
(`getCreateWireServices()`). A caller that passes nothing still gets the
|
|
14
|
+
function its wire services.
|
|
15
|
+
|
|
16
|
+
Before this, only the transports passed a factory. `rpc.invoke`, `rpc.exposed`,
|
|
17
|
+
gateway handlers and webhook sources passed none, so an app function reached
|
|
18
|
+
through them ran on whatever services its caller had already merged. That held
|
|
19
|
+
until `singletonServicesOnly` let a caller skip building wire services: the
|
|
20
|
+
generated `rpcCaller` behind `/rpc/:rpcName` reads no service, was marked, and
|
|
21
|
+
every app function behind it lost `events` and every other wire service. Nested
|
|
22
|
+
RPC therefore gets `resolvedSingletonServices`, not the caller's merged set.
|
|
23
|
+
|
|
24
|
+
Channels are the one caller that legitimately shares wire services: they build
|
|
25
|
+
one set per connection. They hand it over as `wireServices`, which the runner
|
|
26
|
+
uses as-is and does not close, because the connection owns its lifetime.
|
|
27
|
+
|
|
28
|
+
**What this rules out:** passing a caller's merged services down as
|
|
29
|
+
`singletonServices` to carry wire services into a nested call, and deciding in
|
|
30
|
+
one function's meta whether a different function gets wire services.
|
|
31
|
+
`singletonServicesOnly` only ever describes the function it is set on.
|