@pylonsync/functions 0.3.114 → 0.3.118
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/package.json +1 -1
- package/src/runtime.ts +31 -3
- package/src/types.ts +30 -0
package/package.json
CHANGED
package/src/runtime.ts
CHANGED
|
@@ -593,6 +593,25 @@ async function handleCall(msg: CallMessage): Promise<void> {
|
|
|
593
593
|
tenantId:
|
|
594
594
|
((rawAuth.tenantId ?? rawAuth.tenant_id) as string | null | undefined) ??
|
|
595
595
|
null,
|
|
596
|
+
// `elevate` round-trips through the host runtime which mutates
|
|
597
|
+
// the per-call caller_is_admin flag — that's what subsequent
|
|
598
|
+
// scheduler.runAfter() reads. We also mutate the local
|
|
599
|
+
// `auth.isAdmin` so handler code that re-checks `ctx.auth.isAdmin`
|
|
600
|
+
// after elevation sees the new value (it would otherwise stay
|
|
601
|
+
// false even though scheduling now works, which is confusing).
|
|
602
|
+
async elevate(options: { admin: boolean; reason: string }) {
|
|
603
|
+
await rpc(msg.call_id, {
|
|
604
|
+
type: "elevate_auth",
|
|
605
|
+
admin: options.admin,
|
|
606
|
+
reason: options.reason,
|
|
607
|
+
});
|
|
608
|
+
if (options.admin) {
|
|
609
|
+
// Mutate in-place. AuthInfo isn't frozen and handlers hold a
|
|
610
|
+
// reference, so the read on the next line of their code
|
|
611
|
+
// reflects the elevated state.
|
|
612
|
+
(auth as { isAdmin: boolean }).isAdmin = true;
|
|
613
|
+
}
|
|
614
|
+
},
|
|
596
615
|
};
|
|
597
616
|
|
|
598
617
|
// Env is read-only config — safe to expose on every ctx variant. Without
|
|
@@ -712,9 +731,18 @@ async function main() {
|
|
|
712
731
|
const name = basename(file, file.endsWith(".ts") ? ".ts" : ".js");
|
|
713
732
|
try {
|
|
714
733
|
const mod = await import(join(process.cwd(), fnDir, file));
|
|
715
|
-
const def = mod.default as FnDefinition;
|
|
716
|
-
|
|
717
|
-
|
|
734
|
+
const def = mod.default as FnDefinition | undefined;
|
|
735
|
+
// Runtime shape check — a misnamed/malformed export should
|
|
736
|
+
// log + skip, not crash the loader. TS narrows `def.handler`
|
|
737
|
+
// as always-defined because the FnDefinition type says so,
|
|
738
|
+
// but at runtime we don't know what the user actually exported.
|
|
739
|
+
const anyDef = def as unknown as Record<string, unknown> | undefined;
|
|
740
|
+
if (
|
|
741
|
+
anyDef &&
|
|
742
|
+
typeof anyDef.type === "string" &&
|
|
743
|
+
typeof anyDef.handler === "function"
|
|
744
|
+
) {
|
|
745
|
+
registry.set(name, def as FnDefinition);
|
|
718
746
|
}
|
|
719
747
|
} catch (err) {
|
|
720
748
|
console.error(`[functions] Failed to load ${file}:`, err);
|
package/src/types.ts
CHANGED
|
@@ -12,6 +12,36 @@ export interface AuthInfo {
|
|
|
12
12
|
/** Active tenant id (selected organization) for multi-tenant apps.
|
|
13
13
|
* Null when the session hasn't selected one. */
|
|
14
14
|
tenantId: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Promote the call's auth context after the handler has done its
|
|
17
|
+
* own authentication check (HMAC signature verification on a
|
|
18
|
+
* webhook, JWT validation, custom token check). Used by webhook
|
|
19
|
+
* receivers — they're necessarily public (external systems POST
|
|
20
|
+
* to them) but want to schedule internal:true workers after
|
|
21
|
+
* they've proven the request came from a trusted source.
|
|
22
|
+
*
|
|
23
|
+
* The framework does NOT verify the developer actually checked
|
|
24
|
+
* anything before calling this — that's on you. The `reason` is
|
|
25
|
+
* mandatory and gets logged at INFO with the function name so
|
|
26
|
+
* every elevation is auditable.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Github webhook example:
|
|
30
|
+
* const ok = await verifyGithubSignature(secret, rawBody, sig);
|
|
31
|
+
* if (!ok) throw ctx.error("INVALID_SIGNATURE", "bad sig");
|
|
32
|
+
* await ctx.auth.elevate({
|
|
33
|
+
* admin: true,
|
|
34
|
+
* reason: "github webhook hmac verified",
|
|
35
|
+
* });
|
|
36
|
+
* // Now this works — caller_is_admin=true for the gate:
|
|
37
|
+
* await ctx.scheduler.runAfter(0, "deployProject", { deploymentId });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* After calling `elevate({ admin: true })`, `auth.isAdmin` is also
|
|
41
|
+
* mutated to true locally so subsequent reads in the same handler
|
|
42
|
+
* see the new value.
|
|
43
|
+
*/
|
|
44
|
+
elevate(options: { admin: boolean; reason: string }): Promise<void>;
|
|
15
45
|
}
|
|
16
46
|
|
|
17
47
|
// ---------------------------------------------------------------------------
|