@pylonsync/functions 0.4.0 → 0.4.2
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/dist/runtime.d.ts +0 -8
- package/dist/types.d.ts +38 -0
- package/package.json +1 -1
- package/src/runtime.ts +17 -0
- package/src/types.ts +37 -0
package/dist/runtime.d.ts
CHANGED
|
@@ -16,14 +16,6 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { DbReader, DbWriter, Llm, Rooms } from "./types";
|
|
18
18
|
export declare function buildDbReader(callId: string, ssrRead?: boolean): DbReader;
|
|
19
|
-
/**
|
|
20
|
-
* Query-function caller for SSR `serverData.fn(name, args)`. Sends the same
|
|
21
|
-
* `run_fn` frame `ctx.runQuery` uses, on the render's call_id — the host's
|
|
22
|
-
* render loop executes the query with the PAGE's auth context (anonymous on
|
|
23
|
-
* public pages) and rejects anything that isn't a query. Legacy rpc() (no
|
|
24
|
-
* op_id) is correct here: concurrent calls queue settle-chained on the one
|
|
25
|
-
* call_id, matching how ctx.runQuery behaves inside a function.
|
|
26
|
-
*/
|
|
27
19
|
export declare function buildSsrFnCaller(callId: string): (name: string, args?: Record<string, unknown>) => Promise<unknown>;
|
|
28
20
|
export declare function buildDbWriter(callId: string): DbWriter;
|
|
29
21
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -550,6 +550,38 @@ export type MemberRow = Record<string, unknown> & {
|
|
|
550
550
|
* with the caller's identity.
|
|
551
551
|
*/
|
|
552
552
|
export type RequireMember = (orgId: string, opts?: RequireMemberOptions) => Promise<MemberRow>;
|
|
553
|
+
/**
|
|
554
|
+
* Signed file-download URLs. `GET /api/files/<id>` only serves a file to its
|
|
555
|
+
* owner (or an unscoped admin) — `signedUrl` is the supported way to
|
|
556
|
+
* authorize a cross-user read: the host mints a short-lived HMAC-signed
|
|
557
|
+
* path (`/api/files/<id>?sig=...&exp=...`) that the GET handler honors as
|
|
558
|
+
* an alternative to the owner check, anonymously fetchable (works in
|
|
559
|
+
* `<img src>` and download links).
|
|
560
|
+
*
|
|
561
|
+
* WHO gets a URL is the calling function's responsibility — wrap the mint
|
|
562
|
+
* in a membership-gated function so authorization stays app-policy-driven:
|
|
563
|
+
*
|
|
564
|
+
* ```ts
|
|
565
|
+
* export default query({
|
|
566
|
+
* args: { eventId: v.id("Event"), fileId: v.string() },
|
|
567
|
+
* async handler(ctx, args) {
|
|
568
|
+
* const event = await ctx.db.get("Event", args.eventId);
|
|
569
|
+
* await ctx.requireMember(event.orgId, { role: "organizer" });
|
|
570
|
+
* return { url: await ctx.files.signedUrl(args.fileId) };
|
|
571
|
+
* },
|
|
572
|
+
* });
|
|
573
|
+
* ```
|
|
574
|
+
*/
|
|
575
|
+
export interface Files {
|
|
576
|
+
/**
|
|
577
|
+
* Mint a signed download path for a file id. `ttlSecs` defaults to 300
|
|
578
|
+
* and is capped by the host (24h) — a signed URL is a bearer capability,
|
|
579
|
+
* so keep lifetimes short.
|
|
580
|
+
*/
|
|
581
|
+
signedUrl(fileId: string, opts?: {
|
|
582
|
+
ttlSecs?: number;
|
|
583
|
+
}): Promise<string>;
|
|
584
|
+
}
|
|
553
585
|
/** Context for query handlers (read-only).
|
|
554
586
|
*
|
|
555
587
|
* NOTE: `ctx.llm` is NOT exposed here. Queries are reactive: a
|
|
@@ -573,6 +605,8 @@ export interface QueryCtx<R extends AuthRequirement = "optional"> {
|
|
|
573
605
|
error(code: string, message: string): Error;
|
|
574
606
|
/** Assert org membership (optionally a role) — see {@link RequireMember}. */
|
|
575
607
|
requireMember: RequireMember;
|
|
608
|
+
/** Signed file-download URLs — see {@link Files}. */
|
|
609
|
+
files: Files;
|
|
576
610
|
}
|
|
577
611
|
/** Context for mutation handlers (read + write, transactional). */
|
|
578
612
|
export interface MutationCtx<R extends AuthRequirement = "optional"> {
|
|
@@ -588,6 +622,8 @@ export interface MutationCtx<R extends AuthRequirement = "optional"> {
|
|
|
588
622
|
rooms: Rooms;
|
|
589
623
|
/** Per-user OAuth connection registry. */
|
|
590
624
|
connections: Connections;
|
|
625
|
+
/** Signed file-download URLs — see {@link Files}. */
|
|
626
|
+
files: Files;
|
|
591
627
|
/** Create a typed error that triggers rollback. */
|
|
592
628
|
error(code: string, message: string): Error;
|
|
593
629
|
/** Assert org membership (optionally a role) — see {@link RequireMember}. */
|
|
@@ -608,6 +644,8 @@ export interface ActionCtx<R extends AuthRequirement = "optional"> {
|
|
|
608
644
|
connections: Connections;
|
|
609
645
|
/** Environment variables / secrets. */
|
|
610
646
|
env: Record<string, string>;
|
|
647
|
+
/** Signed file-download URLs — see {@link Files}. */
|
|
648
|
+
files: Files;
|
|
611
649
|
/** Run a registered query within its own read transaction. */
|
|
612
650
|
runQuery<T = unknown>(fnName: string, args: Record<string, unknown>): Promise<T>;
|
|
613
651
|
/** Run a registered mutation within its own write transaction. */
|
package/package.json
CHANGED
package/src/runtime.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
EmailAttachment,
|
|
22
22
|
EmailOptions,
|
|
23
23
|
EmailSender,
|
|
24
|
+
Files,
|
|
24
25
|
Stream,
|
|
25
26
|
Scheduler,
|
|
26
27
|
Llm,
|
|
@@ -518,6 +519,19 @@ export function buildDbReader(callId: string, ssrRead = false): DbReader {
|
|
|
518
519
|
* op_id) is correct here: concurrent calls queue settle-chained on the one
|
|
519
520
|
* call_id, matching how ctx.runQuery behaves inside a function.
|
|
520
521
|
*/
|
|
522
|
+
/** `ctx.files` — signed download URLs, minted host-side. */
|
|
523
|
+
function buildFiles(callId: string): Files {
|
|
524
|
+
return {
|
|
525
|
+
async signedUrl(fileId, opts) {
|
|
526
|
+
return rpc(callId, {
|
|
527
|
+
type: "sign_file_url",
|
|
528
|
+
file_id: fileId,
|
|
529
|
+
ttl_secs: opts?.ttlSecs,
|
|
530
|
+
}) as Promise<string>;
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
521
535
|
export function buildSsrFnCaller(
|
|
522
536
|
callId: string,
|
|
523
537
|
): (name: string, args?: Record<string, unknown>) => Promise<unknown> {
|
|
@@ -991,6 +1005,7 @@ function buildActionCtx(
|
|
|
991
1005
|
(err as any).code = code;
|
|
992
1006
|
return err;
|
|
993
1007
|
},
|
|
1008
|
+
files: buildFiles(callId),
|
|
994
1009
|
// Actions have no ctx.db; read membership via the built-in internal query.
|
|
995
1010
|
requireMember: makeRequireMember(auth.userId, (entity, filter) =>
|
|
996
1011
|
rpc(callId, {
|
|
@@ -1133,6 +1148,7 @@ async function handleCall(msg: CallMessage): Promise<void> {
|
|
|
1133
1148
|
requireMember: makeRequireMember(auth.userId, (entity, filter) =>
|
|
1134
1149
|
reader.query(entity, { ...filter, $limit: 1 }),
|
|
1135
1150
|
),
|
|
1151
|
+
files: buildFiles(msg.call_id),
|
|
1136
1152
|
};
|
|
1137
1153
|
break;
|
|
1138
1154
|
}
|
|
@@ -1155,6 +1171,7 @@ async function handleCall(msg: CallMessage): Promise<void> {
|
|
|
1155
1171
|
requireMember: makeRequireMember(auth.userId, (entity, filter) =>
|
|
1156
1172
|
writer.query(entity, { ...filter, $limit: 1 }),
|
|
1157
1173
|
),
|
|
1174
|
+
files: buildFiles(msg.call_id),
|
|
1158
1175
|
};
|
|
1159
1176
|
break;
|
|
1160
1177
|
}
|
package/src/types.ts
CHANGED
|
@@ -644,6 +644,37 @@ export type RequireMember = (
|
|
|
644
644
|
opts?: RequireMemberOptions,
|
|
645
645
|
) => Promise<MemberRow>;
|
|
646
646
|
|
|
647
|
+
/**
|
|
648
|
+
* Signed file-download URLs. `GET /api/files/<id>` only serves a file to its
|
|
649
|
+
* owner (or an unscoped admin) — `signedUrl` is the supported way to
|
|
650
|
+
* authorize a cross-user read: the host mints a short-lived HMAC-signed
|
|
651
|
+
* path (`/api/files/<id>?sig=...&exp=...`) that the GET handler honors as
|
|
652
|
+
* an alternative to the owner check, anonymously fetchable (works in
|
|
653
|
+
* `<img src>` and download links).
|
|
654
|
+
*
|
|
655
|
+
* WHO gets a URL is the calling function's responsibility — wrap the mint
|
|
656
|
+
* in a membership-gated function so authorization stays app-policy-driven:
|
|
657
|
+
*
|
|
658
|
+
* ```ts
|
|
659
|
+
* export default query({
|
|
660
|
+
* args: { eventId: v.id("Event"), fileId: v.string() },
|
|
661
|
+
* async handler(ctx, args) {
|
|
662
|
+
* const event = await ctx.db.get("Event", args.eventId);
|
|
663
|
+
* await ctx.requireMember(event.orgId, { role: "organizer" });
|
|
664
|
+
* return { url: await ctx.files.signedUrl(args.fileId) };
|
|
665
|
+
* },
|
|
666
|
+
* });
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
669
|
+
export interface Files {
|
|
670
|
+
/**
|
|
671
|
+
* Mint a signed download path for a file id. `ttlSecs` defaults to 300
|
|
672
|
+
* and is capped by the host (24h) — a signed URL is a bearer capability,
|
|
673
|
+
* so keep lifetimes short.
|
|
674
|
+
*/
|
|
675
|
+
signedUrl(fileId: string, opts?: { ttlSecs?: number }): Promise<string>;
|
|
676
|
+
}
|
|
677
|
+
|
|
647
678
|
/** Context for query handlers (read-only).
|
|
648
679
|
*
|
|
649
680
|
* NOTE: `ctx.llm` is NOT exposed here. Queries are reactive: a
|
|
@@ -667,6 +698,8 @@ export interface QueryCtx<R extends AuthRequirement = "optional"> {
|
|
|
667
698
|
error(code: string, message: string): Error;
|
|
668
699
|
/** Assert org membership (optionally a role) — see {@link RequireMember}. */
|
|
669
700
|
requireMember: RequireMember;
|
|
701
|
+
/** Signed file-download URLs — see {@link Files}. */
|
|
702
|
+
files: Files;
|
|
670
703
|
}
|
|
671
704
|
|
|
672
705
|
/** Context for mutation handlers (read + write, transactional). */
|
|
@@ -683,6 +716,8 @@ export interface MutationCtx<R extends AuthRequirement = "optional"> {
|
|
|
683
716
|
rooms: Rooms;
|
|
684
717
|
/** Per-user OAuth connection registry. */
|
|
685
718
|
connections: Connections;
|
|
719
|
+
/** Signed file-download URLs — see {@link Files}. */
|
|
720
|
+
files: Files;
|
|
686
721
|
/** Create a typed error that triggers rollback. */
|
|
687
722
|
error(code: string, message: string): Error;
|
|
688
723
|
/** Assert org membership (optionally a role) — see {@link RequireMember}. */
|
|
@@ -704,6 +739,8 @@ export interface ActionCtx<R extends AuthRequirement = "optional"> {
|
|
|
704
739
|
connections: Connections;
|
|
705
740
|
/** Environment variables / secrets. */
|
|
706
741
|
env: Record<string, string>;
|
|
742
|
+
/** Signed file-download URLs — see {@link Files}. */
|
|
743
|
+
files: Files;
|
|
707
744
|
/** Run a registered query within its own read transaction. */
|
|
708
745
|
runQuery<T = unknown>(
|
|
709
746
|
fnName: string,
|