@fitzzero/quickdraw-core 5.1.1 → 5.1.3
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/README.md +1308 -219
- package/dist/{admin-DKwkIh5i.d.ts → admin-CS7JB_UH.d.ts} +2 -2
- package/dist/{adminSchemas-2DUA_hZ8.d.ts → adminSchemas-29cGgrg5.d.ts} +6 -4
- package/dist/{auth-BhWqdyb-.d.ts → auth-DtdYjrZ9.d.ts} +51 -21
- package/dist/{caller-CXTFswBO.d.ts → caller-BTwGl7vm.d.ts} +296 -33
- package/dist/chunk-2FDSKLBO.js +82 -0
- package/dist/chunk-2FDSKLBO.js.map +1 -0
- package/dist/{chunk-RT6OILOI.js → chunk-4D2DOGAW.js} +2 -14
- package/dist/chunk-4D2DOGAW.js.map +1 -0
- package/dist/{chunk-OECZPRPW.js → chunk-64I6MYBC.js} +162 -775
- package/dist/chunk-64I6MYBC.js.map +1 -0
- package/dist/chunk-7473HK24.js +17 -0
- package/dist/chunk-7473HK24.js.map +1 -0
- package/dist/chunk-7Y5ROPXL.js +33 -0
- package/dist/chunk-7Y5ROPXL.js.map +1 -0
- package/dist/chunk-A2RBQD66.js +7 -0
- package/dist/chunk-A2RBQD66.js.map +1 -0
- package/dist/chunk-AK23XSWD.js +16 -0
- package/dist/chunk-AK23XSWD.js.map +1 -0
- package/dist/chunk-ASAMUGHG.js +651 -0
- package/dist/chunk-ASAMUGHG.js.map +1 -0
- package/dist/{chunk-B2P7AZ25.js → chunk-CYE4UBOB.js} +6 -4
- package/dist/chunk-CYE4UBOB.js.map +1 -0
- package/dist/chunk-DKE6QO26.js +136 -0
- package/dist/chunk-DKE6QO26.js.map +1 -0
- package/dist/chunk-DNP65TD5.js +832 -0
- package/dist/chunk-DNP65TD5.js.map +1 -0
- package/dist/{chunk-H23KEFBA.js → chunk-DUBU3BB3.js} +30 -1
- package/dist/chunk-DUBU3BB3.js.map +1 -0
- package/dist/chunk-GYLWEJHW.js +19 -0
- package/dist/chunk-GYLWEJHW.js.map +1 -0
- package/dist/{chunk-SHEGLZEP.js → chunk-I3JCFS7H.js} +34 -13
- package/dist/chunk-I3JCFS7H.js.map +1 -0
- package/dist/{chunk-X5CWPX7H.js → chunk-IIQ3WMJK.js} +13 -2
- package/dist/chunk-IIQ3WMJK.js.map +1 -0
- package/dist/{chunk-RZTP4IEW.js → chunk-JVTONQFI.js} +6339 -4226
- package/dist/chunk-JVTONQFI.js.map +1 -0
- package/dist/{chunk-QEKVYG5F.js → chunk-KKQANRLG.js} +2 -2
- package/dist/chunk-KKQANRLG.js.map +1 -0
- package/dist/{chunk-CDKG4TFA.js → chunk-MPQAT55F.js} +38 -205
- package/dist/chunk-MPQAT55F.js.map +1 -0
- package/dist/chunk-NO4M6PO5.js +101 -0
- package/dist/chunk-NO4M6PO5.js.map +1 -0
- package/dist/{chunk-TBMKJ26R.js → chunk-NW6U45HX.js} +3 -1
- package/dist/chunk-NW6U45HX.js.map +1 -0
- package/dist/chunk-NX2V6SIL.js +202 -0
- package/dist/chunk-NX2V6SIL.js.map +1 -0
- package/dist/{chunk-ZIVPHYRG.js → chunk-NZHA4TZD.js} +72 -175
- package/dist/chunk-NZHA4TZD.js.map +1 -0
- package/dist/{chunk-2PWMUVC7.js → chunk-OCZJ4EYV.js} +518 -27
- package/dist/chunk-OCZJ4EYV.js.map +1 -0
- package/dist/{chunk-REX5YPHX.js → chunk-PHCURHC7.js} +76 -21
- package/dist/chunk-PHCURHC7.js.map +1 -0
- package/dist/{chunk-4YO5H5ON.js → chunk-Q4OGQM5I.js} +4 -10
- package/dist/chunk-Q4OGQM5I.js.map +1 -0
- package/dist/chunk-QSU47XEX.js +13 -0
- package/dist/chunk-QSU47XEX.js.map +1 -0
- package/dist/{chunk-DD4SKCNO.js → chunk-RZXJTDH5.js} +13 -2
- package/dist/chunk-RZXJTDH5.js.map +1 -0
- package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
- package/dist/chunk-SN3OZVEQ.js.map +1 -0
- package/dist/{chunk-4DIQHASK.js → chunk-SQ5VUG77.js} +28 -8
- package/dist/chunk-SQ5VUG77.js.map +1 -0
- package/dist/chunk-STH5DLQP.js +1192 -0
- package/dist/chunk-STH5DLQP.js.map +1 -0
- package/dist/{chunk-QCHKWIRS.js → chunk-VZFJTKIW.js} +13 -13
- package/dist/{chunk-QCHKWIRS.js.map → chunk-VZFJTKIW.js.map} +1 -1
- package/dist/{chunk-VSQBHPWH.js → chunk-X4ZU4QH5.js} +54 -22
- package/dist/chunk-X4ZU4QH5.js.map +1 -0
- package/dist/chunk-XD7SRDUY.js +188 -0
- package/dist/chunk-XD7SRDUY.js.map +1 -0
- package/dist/chunk-XH2IZVLT.js +62 -0
- package/dist/chunk-XH2IZVLT.js.map +1 -0
- package/dist/chunk-Z7I6SONO.js +825 -0
- package/dist/chunk-Z7I6SONO.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +132 -112
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/core.d.ts +387 -0
- package/dist/client/core.js +109 -0
- package/dist/client/index.d.ts +52 -203
- package/dist/client/index.js +59 -218
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-Bc-G8n-U.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
- package/dist/{connection-Cr1BU07W.d.ts → connection-2EeQ_Et0.d.ts} +22 -11
- package/dist/{createServer-CP9eDHZZ.d.ts → createServer-Bx5HOcYK.d.ts} +67 -17
- package/dist/{createTestApp-UKVGEmOf.d.ts → createTestApp-DAMANT79.d.ts} +46 -13
- package/dist/{envelope-Cp3YcVxC.d.ts → envelope-BMBaxppV.d.ts} +216 -10
- package/dist/{http-Djj2Inuu.d.ts → http-YzLAar3w.d.ts} +76 -60
- package/dist/index.d.ts +8 -7
- package/dist/index.js +82 -25
- package/dist/index.js.map +1 -1
- package/dist/{infer-Teo1xpsd.d.ts → infer-Br62cnyW.d.ts} +82 -11
- package/dist/logger-Yz93KzdV.d.ts +30 -0
- package/dist/{metrics-BdBu5o8G.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
- package/dist/origins-kvpxnVcW.d.ts +8 -0
- package/dist/parser.js +2 -1
- package/dist/prisma/index.d.ts +14 -7
- package/dist/prisma/index.js +279 -70
- package/dist/prisma/index.js.map +1 -1
- package/dist/{rateLimit-ZMIZJUZF.js → rateLimit-5RKBFLUR.js} +2 -2
- package/dist/rateLimit-5RKBFLUR.js.map +1 -0
- package/dist/remote-N7OUAGOW.js +15 -0
- package/dist/remote-N7OUAGOW.js.map +1 -0
- package/dist/server/auth/index.d.ts +82 -33
- package/dist/server/auth/index.js +277 -270
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/express/index.d.ts +9 -2
- package/dist/server/express/index.js +1 -1
- package/dist/server/index.d.ts +268 -84
- package/dist/server/index.js +664 -418
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +89 -27
- package/dist/server/mcp/index.js +281 -62
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-DVa8W-yR.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
- package/dist/testing/client.d.ts +14 -14
- package/dist/testing/client.js +19 -10
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +50 -13
- package/dist/testing/index.js +113 -21
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +17 -9
- package/dist/tracked-BR5b2O-U.d.ts +43 -0
- package/dist/{types-Cu0vmx7c.d.ts → types-CLRCQGnO.d.ts} +4 -3
- package/dist/types-tMagHbjy.d.ts +151 -0
- package/dist/utils/index.d.ts +16 -4
- package/dist/utils/index.js +6 -3
- package/package.json +5 -1
- package/dist/chunk-2PWMUVC7.js.map +0 -1
- package/dist/chunk-4DIQHASK.js.map +0 -1
- package/dist/chunk-4YO5H5ON.js.map +0 -1
- package/dist/chunk-B2P7AZ25.js.map +0 -1
- package/dist/chunk-CDKG4TFA.js.map +0 -1
- package/dist/chunk-DD4SKCNO.js.map +0 -1
- package/dist/chunk-H23KEFBA.js.map +0 -1
- package/dist/chunk-OECZPRPW.js.map +0 -1
- package/dist/chunk-QEKVYG5F.js.map +0 -1
- package/dist/chunk-REX5YPHX.js.map +0 -1
- package/dist/chunk-RT6OILOI.js.map +0 -1
- package/dist/chunk-RZTP4IEW.js.map +0 -1
- package/dist/chunk-SHEGLZEP.js.map +0 -1
- package/dist/chunk-TBMKJ26R.js.map +0 -1
- package/dist/chunk-VSQBHPWH.js.map +0 -1
- package/dist/chunk-X5CWPX7H.js.map +0 -1
- package/dist/chunk-YSWH776Y.js +0 -13
- package/dist/chunk-YSWH776Y.js.map +0 -1
- package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
- package/dist/chunk-ZIVPHYRG.js.map +0 -1
- package/dist/logger-BY28acW-.d.ts +0 -16
- /package/dist/{rateLimit-ZMIZJUZF.js.map → client/core.js.map} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { A as AccessLevel, J as AnyContract, y as CollectionWhere, O as OrderBy, k as MethodKind, d as StandardSchemaV1, ag as EventName, ah as EventPayloadOf, aB as ServerChannelName, ab as ChannelPayloadOf, aa as ChannelName, aC as ServerStreamName, az as StreamName, am as IsScopedStream, ay as StreamItemOf, ae as ContractMap, ap as MethodName, al as InputOf, ar as OutputOf } from './infer-Br62cnyW.js';
|
|
2
|
+
import { P as Principal, Q as QuickdrawTypes, M as MaybePromise, b as PrincipalOf, a as McpContext, T as Transport, C as ContextExtensionOf, c as McpContextOf, D as DbOf } from './types-CLRCQGnO.js';
|
|
3
|
+
import { L as Logger } from './logger-Yz93KzdV.js';
|
|
4
|
+
import { aA as Version, as as Revision } from './envelope-BMBaxppV.js';
|
|
5
5
|
|
|
6
6
|
/** What a development warning is about. */
|
|
7
|
-
type DevWarningKind = "n-plus-one" | "unbounded-read" | "oversized-response" | "nested-write" | "ambient-write" | "batch-read" | "batch-create-many" | "repeated-call" | "tiered-field-in-output" | "resolver-without-reads";
|
|
7
|
+
type DevWarningKind = "n-plus-one" | "unbounded-read" | "oversized-response" | "nested-write" | "ambient-write" | "batch-read" | "batch-create-many" | "repeated-call" | "tiered-field-in-output" | "resolver-without-reads" | "untracked-access-model" | "shared-principal" | "channel-without-requires";
|
|
8
8
|
/** One development warning. */
|
|
9
9
|
interface DevWarning {
|
|
10
10
|
readonly kind: DevWarningKind;
|
|
@@ -14,8 +14,8 @@ interface DevWarning {
|
|
|
14
14
|
/**
|
|
15
15
|
* What the warning is about within its call, for the once-only rule: the
|
|
16
16
|
* model of an ambient write, the model, field and operation of a nested
|
|
17
|
-
* write, the connection of a repeated call
|
|
18
|
-
* once per call site.
|
|
17
|
+
* write, the connection of a repeated call, the `ctx` member a shared run
|
|
18
|
+
* read. Empty for the kinds that are once per call site.
|
|
19
19
|
*/
|
|
20
20
|
readonly subject?: string;
|
|
21
21
|
/** What happened and what to do instead. */
|
|
@@ -95,11 +95,25 @@ interface StorageAdapter {
|
|
|
95
95
|
registerInterest(model: string, columns: readonly string[]): void;
|
|
96
96
|
/** The columns registered for `model`. */
|
|
97
97
|
interestOf(model: string): readonly string[];
|
|
98
|
+
/**
|
|
99
|
+
* Declares that rows of `model` are the entries of a `via` collection over
|
|
100
|
+
* the junction `link` names: a `delete` or `deleteMany` of `model` reads
|
|
101
|
+
* the junction rows linking the rows it deletes just before, through the
|
|
102
|
+
* open transaction when there is one, and reports them in each write's
|
|
103
|
+
* `links`, so a flush tells only the scopes a deleted entry was linked to
|
|
104
|
+
* (the 5.1 audit's COLL-3). A link committed between that read and the
|
|
105
|
+
* delete is not read (`prisma/links.ts`). Optional: without it a deleted
|
|
106
|
+
* entry left scopes nobody can name, and every subscribed scope of its
|
|
107
|
+
* collection is told it was removed.
|
|
108
|
+
*/
|
|
109
|
+
registerLinks?(model: string, link: LinkInterest): void;
|
|
110
|
+
/** The junctions registered for `model` (`registerLinks`). */
|
|
111
|
+
linksOf?(model: string): readonly LinkInterest[];
|
|
98
112
|
/**
|
|
99
113
|
* True where the caller runs inside an open transaction of this adapter,
|
|
100
|
-
* whose reads see writes that may still roll back. The access cache
|
|
101
|
-
*
|
|
102
|
-
* across requests.
|
|
114
|
+
* whose reads see writes that may still roll back. The access cache
|
|
115
|
+
* neither reads nor keeps anything there; an adapter without this method
|
|
116
|
+
* gets no access cache across requests.
|
|
103
117
|
*/
|
|
104
118
|
inTransaction?(): boolean;
|
|
105
119
|
/**
|
|
@@ -110,6 +124,15 @@ interface StorageAdapter {
|
|
|
110
124
|
* cursor shows holding null.
|
|
111
125
|
*/
|
|
112
126
|
nullable?(model: string, column: string): Promise<boolean>;
|
|
127
|
+
/**
|
|
128
|
+
* Whether the adapter can key `model`'s writes: false for a model without
|
|
129
|
+
* an `id` column (a table keyed by a composite `@@id`), whose writes are
|
|
130
|
+
* never tracked. `createServer` asks it, at start, for every model an
|
|
131
|
+
* access policy, a membership table or a `via` junction depends on, and
|
|
132
|
+
* names each it cannot track (the `untracked-access-model` warning). An
|
|
133
|
+
* adapter without it is not asked.
|
|
134
|
+
*/
|
|
135
|
+
trackable?(model: string): Promise<boolean>;
|
|
113
136
|
/** The units of work that record this adapter's writes; the dispatcher uses them. */
|
|
114
137
|
readonly unitOfWork: UnitOfWorkFactory;
|
|
115
138
|
}
|
|
@@ -119,6 +142,17 @@ interface StorageAdapter {
|
|
|
119
142
|
* `trackPrisma` still carries it.
|
|
120
143
|
*/
|
|
121
144
|
declare function storageOf(db: unknown): StorageAdapter | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* A junction whose rows link an entry model's rows to the scopes of a `via`
|
|
147
|
+
* collection (`registerLinks`): its model, as the client names it, the
|
|
148
|
+
* column holding the entry's id, and the columns a delete reads with it (the
|
|
149
|
+
* scope column, and the role columns of a members table's rule).
|
|
150
|
+
*/
|
|
151
|
+
interface LinkInterest {
|
|
152
|
+
readonly model: string;
|
|
153
|
+
readonly entry: string;
|
|
154
|
+
readonly columns: readonly string[];
|
|
155
|
+
}
|
|
122
156
|
|
|
123
157
|
/** A principal's level on one row: an access level, or `null` for none. */
|
|
124
158
|
type RowLevel = AccessLevel | null;
|
|
@@ -301,7 +335,22 @@ type CollectionScope = {
|
|
|
301
335
|
readonly scope: string;
|
|
302
336
|
/** A junction write sends its entry again to every scope that still holds it (`via`'s `refreshEntry`). */
|
|
303
337
|
readonly refreshEntry: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* The junction is the item service's `members` table: only links whose
|
|
340
|
+
* role gives at least the collection's `access` count, by one rule per
|
|
341
|
+
* role column the policy's `members` reads of the table use (a link
|
|
342
|
+
* any rule counts counts), and a write of a role column can move the
|
|
343
|
+
* link in or out.
|
|
344
|
+
*/
|
|
345
|
+
readonly levels?: readonly LinkLevel[];
|
|
304
346
|
};
|
|
347
|
+
/** One role rule of a `via` scope over the item service's `members` table. */
|
|
348
|
+
interface LinkLevel {
|
|
349
|
+
/** The junction's role column. */
|
|
350
|
+
readonly column: string;
|
|
351
|
+
/** The stored roles that give at least the collection's `access`; none: no link counts by this column. */
|
|
352
|
+
readonly roles: readonly string[];
|
|
353
|
+
}
|
|
305
354
|
/** One collection of a service: its contract declaration and the service's option, checked. */
|
|
306
355
|
interface ServiceCollection {
|
|
307
356
|
readonly name: string;
|
|
@@ -319,6 +368,14 @@ interface ServiceCollection {
|
|
|
319
368
|
readonly access: AccessLevel;
|
|
320
369
|
/** The contract of the rows scope values are ids of; `undefined` for a `"self"` scope. */
|
|
321
370
|
readonly anchor: AnyContract | undefined;
|
|
371
|
+
/**
|
|
372
|
+
* The service-wide grant on the collection's service every subscriber needs
|
|
373
|
+
* besides the anchor or self check (`requires: { service: L }`);
|
|
374
|
+
* `undefined` for none.
|
|
375
|
+
*/
|
|
376
|
+
readonly requires: {
|
|
377
|
+
readonly service: AccessLevel;
|
|
378
|
+
} | undefined;
|
|
322
379
|
/** More changed rows than this for one scope in one flush go out as one `reset`. */
|
|
323
380
|
readonly bulkThreshold: number;
|
|
324
381
|
/**
|
|
@@ -389,6 +446,14 @@ interface ServiceMethod {
|
|
|
389
446
|
* caller is left to the form.
|
|
390
447
|
*/
|
|
391
448
|
readonly kinds: readonly string[] | undefined;
|
|
449
|
+
/**
|
|
450
|
+
* The method's requirement on the caller's claims (RFC 0003 section 4.1,
|
|
451
|
+
* `access/claims.ts`), checked right after `kinds`, outside the access
|
|
452
|
+
* engine: `UNAUTHENTICATED` without a principal, `FORBIDDEN` unless it
|
|
453
|
+
* returns `true`. It adds to the access form, and no grant passes it, a
|
|
454
|
+
* service-wide `Admin` grant included. `undefined` when not declared.
|
|
455
|
+
*/
|
|
456
|
+
readonly claims: ClaimsCheck | undefined;
|
|
392
457
|
readonly handler: AnyHandler;
|
|
393
458
|
readonly share: ShareMode | undefined;
|
|
394
459
|
/** How long a shared result is reused after its run, in milliseconds. */
|
|
@@ -466,6 +531,8 @@ interface Service<T extends QuickdrawTypes = QuickdrawTypes, C extends AnyContra
|
|
|
466
531
|
* rooms. `undefined` when the service declares none.
|
|
467
532
|
*/
|
|
468
533
|
readonly onRoomLeave: RoomLeaveHandler | undefined;
|
|
534
|
+
/** The service's own `onHostLeave`: `createServer` runs it, beside the server's own, when a room's host leaves. */
|
|
535
|
+
readonly onHostLeave?: HostLeaveHandler | undefined;
|
|
469
536
|
/** Type-only: the app types the service was defined with. Never set. */
|
|
470
537
|
readonly "~types"?: T;
|
|
471
538
|
}
|
|
@@ -524,6 +591,11 @@ interface ScopeAccess<Input = never> {
|
|
|
524
591
|
readonly service?: never;
|
|
525
592
|
readonly kind?: never;
|
|
526
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* A method's `claims`, as the pipeline calls it (`claims.ts`): `true` when
|
|
596
|
+
* the caller's claims allow the call with `input`.
|
|
597
|
+
*/
|
|
598
|
+
type ClaimsCheck = (claims: Principal["claims"], input: unknown) => boolean;
|
|
527
599
|
/**
|
|
528
600
|
* `custom(fn)`: the call passes when `fn(ctx, input)` resolves `true`. The
|
|
529
601
|
* caller must be authenticated before `fn` runs, so `ctx.principal` is never
|
|
@@ -598,11 +670,27 @@ interface RowAccess {
|
|
|
598
670
|
allows(form: EntryAccess | ScopeAccess, request: AccessRequest): MaybePromise<boolean>;
|
|
599
671
|
}
|
|
600
672
|
|
|
673
|
+
/**
|
|
674
|
+
* A row whose access may have changed: who may now see more or less of it.
|
|
675
|
+
* `id` is the row (the anchor subscriptions record); without it, any row of
|
|
676
|
+
* the service. `userId` is the user whose level may have changed; without it,
|
|
677
|
+
* anyone's.
|
|
678
|
+
*/
|
|
679
|
+
interface AccessChange {
|
|
680
|
+
/** The service name. */
|
|
681
|
+
readonly service: string;
|
|
682
|
+
readonly id?: string;
|
|
683
|
+
readonly userId?: string;
|
|
684
|
+
}
|
|
685
|
+
/** Receives access changes; a promise it returns is awaited before the flush moves on. */
|
|
686
|
+
type AccessChangeListener = (change: AccessChange) => void | PromiseLike<void>;
|
|
687
|
+
|
|
601
688
|
/**
|
|
602
689
|
* Who is online and who is in a room (RFC 0003 section 12.5): `ctx.presence`,
|
|
603
690
|
* `server.presence`, `qd.presence`. Every answer is a promise: on a server
|
|
604
691
|
* with the in-memory adapter it comes from this process's sockets; behind a
|
|
605
|
-
* cluster adapter (Redis) it asks
|
|
692
|
+
* cluster adapter (Redis) it asks the other nodes too, which answer with
|
|
693
|
+
* user ids (or whether they have a socket of the user) alone.
|
|
606
694
|
*/
|
|
607
695
|
interface Presence {
|
|
608
696
|
/** True while the user has a socket connected (on any node). */
|
|
@@ -614,9 +702,14 @@ interface Presence {
|
|
|
614
702
|
* the 100,000 users seen last).
|
|
615
703
|
*/
|
|
616
704
|
lastSeen(userId: string): Promise<number | null>;
|
|
617
|
-
/**
|
|
705
|
+
/**
|
|
706
|
+
* How many users have a socket in app room `room` that `ctx.rooms.join`
|
|
707
|
+
* put there (a raw `socket.join` does not count on this process).
|
|
708
|
+
* Anonymous sockets do not count. A framework room (`qd:`, `user:`) or a
|
|
709
|
+
* name with the shape of a socket id is `VALIDATION`.
|
|
710
|
+
*/
|
|
618
711
|
count(room: string): Promise<number>;
|
|
619
|
-
/** The ids of the users
|
|
712
|
+
/** The ids of the users `count` counts in `room`, each once. */
|
|
620
713
|
users(room: string): Promise<string[]>;
|
|
621
714
|
}
|
|
622
715
|
/** Whose sockets `rooms.leave(room, target)` takes out of a room: every socket of one user. */
|
|
@@ -639,8 +732,9 @@ interface ServerRooms {
|
|
|
639
732
|
/**
|
|
640
733
|
* Sends the contract's event to every socket in `room` as `qd:event`, on
|
|
641
734
|
* every node. The payload is checked against the event's schema first: a
|
|
642
|
-
* payload that fails it throws `INTERNAL` and nothing is sent.
|
|
643
|
-
*
|
|
735
|
+
* payload that fails it throws `INTERNAL` and nothing is sent. A room of
|
|
736
|
+
* the framework's (`qd:`, `user:`) throws `VALIDATION`: send to a user's
|
|
737
|
+
* sockets with `emitToUser`. Without a server it does nothing more.
|
|
644
738
|
*/
|
|
645
739
|
emit<C extends AnyContract, E extends EventName<C>>(room: string, contract: C, event: E, payload: EventPayloadOf<C, E>): void;
|
|
646
740
|
/** `emit` to every socket of one user (their `user:{userId}` room), on every node. */
|
|
@@ -670,6 +764,19 @@ interface ServerRooms {
|
|
|
670
764
|
* it is 0.
|
|
671
765
|
*/
|
|
672
766
|
size(room: string): number;
|
|
767
|
+
/**
|
|
768
|
+
* The host of app room `room` (host mode, RFC 0003 section 12.5): the id
|
|
769
|
+
* of the socket that serves the room's host-served channels and streams,
|
|
770
|
+
* or `null` when it has none. This node's record, which every node of a
|
|
771
|
+
* cluster keeps. Room names are checked as `join` checks them.
|
|
772
|
+
*/
|
|
773
|
+
hostOf(room: string): string | null;
|
|
774
|
+
/**
|
|
775
|
+
* Leaves app room `room` without a host, on every node: its sockets hear
|
|
776
|
+
* `qd:host [room, null]`. Nothing happens for a room without one, and
|
|
777
|
+
* `onHostLeave` does not run (the host did not leave).
|
|
778
|
+
*/
|
|
779
|
+
clearHost(room: string): void;
|
|
673
780
|
}
|
|
674
781
|
/**
|
|
675
782
|
* `ctx.rooms` (RFC 0003 sections 3, 12.5 and 15): app-defined rooms the
|
|
@@ -685,12 +792,28 @@ interface ContextRooms extends ServerRooms {
|
|
|
685
792
|
* nothing, for a call that did not arrive over a socket (HTTP, MCP, in
|
|
686
793
|
* process). Room names starting with `qd:` or `user:` are refused with
|
|
687
794
|
* `VALIDATION` (those rooms are joined only through their authorized
|
|
688
|
-
* paths), as are
|
|
689
|
-
*
|
|
795
|
+
* paths), as are names with the shape of a Socket.IO socket id (20
|
|
796
|
+
* letters, digits, `_` or `-`: every socket is alone in the room of its
|
|
797
|
+
* id), empty names and names over 256 characters; a socket in 100 app
|
|
798
|
+
* rooms is refused another with `CONFLICT`.
|
|
690
799
|
*/
|
|
691
800
|
join(room: string): boolean;
|
|
692
801
|
/** Takes the calling socket out of an app room; `false` when it was not in it, or the call has no socket. */
|
|
693
802
|
leave(room: string): boolean;
|
|
803
|
+
/**
|
|
804
|
+
* Makes the calling socket the host of app room `room` (host mode, RFC
|
|
805
|
+
* 0003 section 12.5): the room's sockets hear `qd:host [room, socketId]`
|
|
806
|
+
* on every node, and so does every socket that joins it later, until the
|
|
807
|
+
* host leaves the room (`onHostLeave` runs), another socket is made its
|
|
808
|
+
* host, or `clearHost` runs. Its peers then send the room's host-served
|
|
809
|
+
* channels to it and subscribe to its host-served streams over peer links.
|
|
810
|
+
* Returns `false`, doing nothing, for a call that did not arrive over a
|
|
811
|
+
* socket. The socket must be in the room (`FORBIDDEN`) and signed in
|
|
812
|
+
* (`UNAUTHENTICATED`), and the server must serve peer links
|
|
813
|
+
* (`createServer({ peers: true })`, else `INTERNAL`). Room names are
|
|
814
|
+
* checked as `join` checks them.
|
|
815
|
+
*/
|
|
816
|
+
setHost(room: string): boolean;
|
|
694
817
|
/** See {@link ServerRooms.leave}: every socket of the user, on every node, from any call. */
|
|
695
818
|
leave(room: string, target: RoomTarget): Promise<void>;
|
|
696
819
|
}
|
|
@@ -734,10 +857,31 @@ interface RoomLeave<P = Principal> {
|
|
|
734
857
|
* error it throws is logged; a server's `close()` waits for it.
|
|
735
858
|
*/
|
|
736
859
|
type RoomLeaveHandler<P = Principal> = (leave: RoomLeave<P>, ctx: RunContext) => void | PromiseLike<void>;
|
|
860
|
+
/** The host of an app room left it (host mode): what `onHostLeave` hears. */
|
|
861
|
+
interface HostLeave<P = Principal> {
|
|
862
|
+
readonly room: string;
|
|
863
|
+
/** The host socket that left. */
|
|
864
|
+
readonly socketId: string;
|
|
865
|
+
/** Its principal. */
|
|
866
|
+
readonly principal: P | null;
|
|
867
|
+
/** Its own leave, a removal, or a disconnect. */
|
|
868
|
+
readonly reason: RoomLeaveReason;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* `onHostLeave` (a service's, beside `onRoomLeave`, and `createServer`'s):
|
|
872
|
+
* called once when the host of an app room leaves it, is taken out of it or
|
|
873
|
+
* disconnects, on the node that held it, after the room heard
|
|
874
|
+
* `qd:host [room, null]`, in a unit of work of its own. The room has no
|
|
875
|
+
* host until a method sets one again: the app decides what follows (another
|
|
876
|
+
* host, the end of the game).
|
|
877
|
+
*/
|
|
878
|
+
type HostLeaveHandler<P = Principal> = (leave: HostLeave<P>, ctx: RunContext) => void | PromiseLike<void>;
|
|
737
879
|
/** The arguments of a stream's `push`: `(scope, item)` for a scoped stream, `(item)` for a global one. */
|
|
738
880
|
type StreamPushArgs<C extends AnyContract, K extends StreamName<C>> = IsScopedStream<C, K> extends true ? [scope: string, item: StreamItemOf<C, K>] : [item: StreamItemOf<C, K>];
|
|
739
881
|
/** The arguments of a stream's `pushMany`: `(scope, items)` for a scoped stream, `(items)` for a global one. */
|
|
740
882
|
type StreamPushManyArgs<C extends AnyContract, K extends StreamName<C>> = IsScopedStream<C, K> extends true ? [scope: string, items: readonly StreamItemOf<C, K>[]] : [items: readonly StreamItemOf<C, K>[]];
|
|
883
|
+
/** The arguments of a stream's `subscribers`: `(scope)` for a scoped stream, `()` for a global one. */
|
|
884
|
+
type StreamSubscribersArgs<C extends AnyContract, K extends StreamName<C>> = IsScopedStream<C, K> extends true ? [scope: string] : [];
|
|
741
885
|
/** What `qd.stream(contract, name)` returns. */
|
|
742
886
|
interface StreamHandle<C extends AnyContract, K extends StreamName<C>> {
|
|
743
887
|
/**
|
|
@@ -754,6 +898,19 @@ interface StreamHandle<C extends AnyContract, K extends StreamName<C>> {
|
|
|
754
898
|
* with nothing kept or sent. The batch form of a `push` in a loop.
|
|
755
899
|
*/
|
|
756
900
|
pushMany(...args: StreamPushManyArgs<C, K>): void;
|
|
901
|
+
/**
|
|
902
|
+
* How many sockets subscribe to the feed (`subscribers(scope)`, or
|
|
903
|
+
* `subscribers()` for a global stream), on every node, anonymous ones
|
|
904
|
+
* included: a job skips the work nobody watches. These are the sockets in
|
|
905
|
+
* the feed's room, the ones a push reaches: a subscribe counts from when it
|
|
906
|
+
* joins the feed, once it is authorized. Behind a cluster adapter the
|
|
907
|
+
* other nodes answer one request with a count each: an answer that does
|
|
908
|
+
* not arrive within `cluster.timeoutMs` counts as 0, and so does a node
|
|
909
|
+
* of an earlier version, which does not know the request. A host-served stream (`serve: "host"`) has no feed on
|
|
910
|
+
* the server, so, as for `push`, its handle refuses this with a
|
|
911
|
+
* `TypeError`: only the host's client knows who subscribes.
|
|
912
|
+
*/
|
|
913
|
+
subscribers(...args: StreamSubscribersArgs<C, K>): Promise<number>;
|
|
757
914
|
}
|
|
758
915
|
/**
|
|
759
916
|
* What a stream's `seed` function receives besides the scope: who is
|
|
@@ -815,7 +972,7 @@ type StreamValidation = "always" | "development";
|
|
|
815
972
|
* subset): a seed computed at subscribe time, and when items are checked.
|
|
816
973
|
*/
|
|
817
974
|
type StreamOptions<T extends QuickdrawTypes, C extends AnyContract> = {
|
|
818
|
-
readonly [K in
|
|
975
|
+
readonly [K in ServerStreamName<C>]?: StreamImplementation<T, C, K>;
|
|
819
976
|
};
|
|
820
977
|
/** A stream's `seed` function as the subscribe path calls it, whatever its declared types. */
|
|
821
978
|
type AnyStreamSeed = (scope: string | undefined, ctx: StreamSeedContext) => unknown;
|
|
@@ -850,7 +1007,12 @@ interface ChannelContext<P = Principal, Room extends string | undefined = string
|
|
|
850
1007
|
type ChannelHandler<T extends QuickdrawTypes, Payload, Room extends string | undefined = string | undefined> = (payload: Payload, ctx: ChannelContext<PrincipalOf<T>, Room>) => void | PromiseLike<void>;
|
|
851
1008
|
/** One channel's implementation in `defineService`: its handler, or `{ access, handler }`. */
|
|
852
1009
|
type ChannelImplementation<T extends QuickdrawTypes, Payload, Room extends string | undefined = string | undefined> = ChannelHandler<T, Payload, Room> | {
|
|
853
|
-
/**
|
|
1010
|
+
/**
|
|
1011
|
+
* Default `"authenticated"`. A channel whose contract declares no
|
|
1012
|
+
* `requires` takes a message from every signed-in socket: declaring
|
|
1013
|
+
* `access` (even `"authenticated"`) says so on purpose, and silences
|
|
1014
|
+
* the `channel-without-requires` warning the server raises otherwise.
|
|
1015
|
+
*/
|
|
854
1016
|
readonly access?: ChannelAccess;
|
|
855
1017
|
readonly handler: ChannelHandler<T, Payload, Room>;
|
|
856
1018
|
};
|
|
@@ -860,12 +1022,15 @@ type ChannelRoomOf<C extends AnyContract, K extends ChannelName<C>> = C["channel
|
|
|
860
1022
|
readonly room: string | object;
|
|
861
1023
|
};
|
|
862
1024
|
} ? string : undefined;
|
|
863
|
-
/**
|
|
1025
|
+
/**
|
|
1026
|
+
* `defineService`'s `channels`: one implementation per channel of the
|
|
1027
|
+
* contract the server handles (a host-served one takes none).
|
|
1028
|
+
*/
|
|
864
1029
|
type ChannelOptions<T extends QuickdrawTypes, C extends AnyContract> = {
|
|
865
|
-
readonly [K in
|
|
1030
|
+
readonly [K in ServerChannelName<C>]: ChannelImplementation<T, ChannelPayloadOf<C, K>, ChannelRoomOf<C, K>>;
|
|
866
1031
|
};
|
|
867
1032
|
/** `{ channels }` is required when the contract declares channels: every message needs a handler. */
|
|
868
|
-
type ChannelsRequired<C extends AnyContract> = [
|
|
1033
|
+
type ChannelsRequired<C extends AnyContract> = [ServerChannelName<C>] extends [never] ? unknown : {
|
|
869
1034
|
readonly channels: unknown;
|
|
870
1035
|
};
|
|
871
1036
|
/**
|
|
@@ -920,6 +1085,12 @@ interface ServiceChannel {
|
|
|
920
1085
|
readonly prefix: string;
|
|
921
1086
|
} | undefined;
|
|
922
1087
|
readonly access: ChannelAccess;
|
|
1088
|
+
/**
|
|
1089
|
+
* True when the service named `access` for the channel (`{ access,
|
|
1090
|
+
* handler }`), not a bare handler or `{ handler }`: a channel without
|
|
1091
|
+
* `requires` warns `channel-without-requires` unless it did.
|
|
1092
|
+
*/
|
|
1093
|
+
readonly accessDeclared: boolean;
|
|
923
1094
|
/**
|
|
924
1095
|
* The kinds of principal the channel's service admits, as a set for the
|
|
925
1096
|
* per-message check: a message from a socket of another kind, or without
|
|
@@ -950,6 +1121,8 @@ interface ServiceStream {
|
|
|
950
1121
|
readonly access: AccessForm | undefined;
|
|
951
1122
|
/** The contract's `access: { room }`: the app room a subscriber's socket must be in. */
|
|
952
1123
|
readonly room: StreamRoomAccess | undefined;
|
|
1124
|
+
/** True for a stream the room's host pushes (`serve: "host"`): the server neither pushes nor subscribes it. */
|
|
1125
|
+
readonly host: boolean;
|
|
953
1126
|
}
|
|
954
1127
|
/** A stream's `access: { room }`, compiled: the app room a subscriber's socket must be in. */
|
|
955
1128
|
type StreamRoomAccess = {
|
|
@@ -972,10 +1145,10 @@ interface TouchOptions {
|
|
|
972
1145
|
* `ctx.services`: in-process callers for the app's services (RFC 0003
|
|
973
1146
|
* sections 3 and 10), by service name: `ctx.services.projectService.get(input)`.
|
|
974
1147
|
* A call runs the whole pipeline (input check, access, handler, output
|
|
975
|
-
* check) as the calling principal with transport `"internal"
|
|
976
|
-
*
|
|
977
|
-
* cancelled with `ctx.signal`. Typed by
|
|
978
|
-
* like `qd.caller`; untyped without them.
|
|
1148
|
+
* check) as the calling principal with transport `"internal"` and the
|
|
1149
|
+
* calling method's `ctx.mcp`, its writes join the calling method's unit of
|
|
1150
|
+
* work (one flush for both), and it is cancelled with `ctx.signal`. Typed by
|
|
1151
|
+
* the `contracts` of the app's types, like `qd.caller`; untyped without them.
|
|
979
1152
|
*/
|
|
980
1153
|
type ContextServices<T extends QuickdrawTypes = QuickdrawTypes> = CallerFor<T>;
|
|
981
1154
|
|
|
@@ -1012,7 +1185,10 @@ interface BaseContext<P = Principal, M = McpContext> {
|
|
|
1012
1185
|
/**
|
|
1013
1186
|
* The fields the MCP bridge's `context` option produced for this call, such
|
|
1014
1187
|
* as the scopes of the agent's token; typed by `QuickdrawTypes["mcp"]`.
|
|
1015
|
-
* `undefined` unless the call arrived over MCP
|
|
1188
|
+
* `undefined` unless the call arrived over MCP or was made through the
|
|
1189
|
+
* `ctx.services` of a call that has it (nested calls too), which passes
|
|
1190
|
+
* the same object on. Such a call's `transport` is `"internal"`: to tell a
|
|
1191
|
+
* token's call apart, read `ctx.mcp`, not `ctx.transport`.
|
|
1016
1192
|
*/
|
|
1017
1193
|
readonly mcp?: M;
|
|
1018
1194
|
/**
|
|
@@ -1024,10 +1200,20 @@ interface BaseContext<P = Principal, M = McpContext> {
|
|
|
1024
1200
|
*/
|
|
1025
1201
|
touch(target: string | AnyContract, ids: string | readonly string[], options?: TouchOptions): void;
|
|
1026
1202
|
/**
|
|
1027
|
-
* In-process callers for the app's services, as this call's principal
|
|
1028
|
-
* `await ctx.services.projectService.get({ id })`. See
|
|
1203
|
+
* In-process callers for the app's services, as this call's principal and
|
|
1204
|
+
* with its `ctx.mcp`: `await ctx.services.projectService.get({ id })`. See
|
|
1205
|
+
* {@link ContextServices}.
|
|
1029
1206
|
*/
|
|
1030
1207
|
readonly services: ContextServices;
|
|
1208
|
+
/**
|
|
1209
|
+
* The caller's levels on rows of the app's services, as the access forms
|
|
1210
|
+
* count them (RFC 0003 section 4.3): `dispatcher.access` bound to
|
|
1211
|
+
* `ctx.principal`, with the grants this call runs with. For a `custom`
|
|
1212
|
+
* form that checks a row its key names only sometimes, or a parent its
|
|
1213
|
+
* input names: `await ctx.access.can(project, input.projectId, "Moderate")`.
|
|
1214
|
+
* See {@link ContextAccess}.
|
|
1215
|
+
*/
|
|
1216
|
+
readonly access: ContextAccess;
|
|
1031
1217
|
/**
|
|
1032
1218
|
* App-defined rooms the calling socket joins and leaves (`join` answers
|
|
1033
1219
|
* `false` for a call without a socket), and typed room events: `emit` and
|
|
@@ -1112,19 +1298,32 @@ interface WriteRecord {
|
|
|
1112
1298
|
/** The written row's id. */
|
|
1113
1299
|
readonly id: string;
|
|
1114
1300
|
readonly op: "create" | "update" | "delete";
|
|
1115
|
-
/**
|
|
1301
|
+
/**
|
|
1302
|
+
* The fields the write set; `[ANY_FIELD]` when they are unknown. An update
|
|
1303
|
+
* whose data held a nested write, which may set a foreign key through a
|
|
1304
|
+
* relation, lists every interested column of the model besides its keys.
|
|
1305
|
+
*/
|
|
1116
1306
|
readonly fields: readonly string[];
|
|
1117
1307
|
/**
|
|
1118
1308
|
* The row before the unit of work wrote it, for its interested columns: an
|
|
1119
1309
|
* update's old values of the interested columns it changed (read first,
|
|
1120
|
-
* narrowly), or
|
|
1310
|
+
* narrowly), or of every interested column when its data held a nested
|
|
1311
|
+
* write, or a delete's values. A row the unit created has none.
|
|
1121
1312
|
*/
|
|
1122
1313
|
readonly before?: Readonly<Record<string, unknown>>;
|
|
1123
1314
|
/** The interested columns' values after the write, when the write returned them. */
|
|
1124
1315
|
readonly after?: Readonly<Record<string, unknown>>;
|
|
1316
|
+
/**
|
|
1317
|
+
* On a delete of a model whose junction links are registered
|
|
1318
|
+
* (`StorageAdapter.registerLinks`): per junction model, the junction rows
|
|
1319
|
+
* that linked the row just before the delete (possibly none), with the
|
|
1320
|
+
* registered columns. Absent when the links were not read.
|
|
1321
|
+
*/
|
|
1322
|
+
readonly links?: Readonly<Record<string, readonly Readonly<Record<string, unknown>>[]>>;
|
|
1125
1323
|
/**
|
|
1126
1324
|
* Set on a `create` an `upsert` recorded without reading whether its row
|
|
1127
|
-
* existed (its `update` set no interested column
|
|
1325
|
+
* existed (its `update` set no interested column and held no nested
|
|
1326
|
+
* write, or the model has no interested columns): the row may have been
|
|
1128
1327
|
* updated rather than created. Only merging reads it: a row created and
|
|
1129
1328
|
* deleted in one unit is dropped from the flush only when its create is
|
|
1130
1329
|
* certain.
|
|
@@ -1222,6 +1421,70 @@ interface FlushSink {
|
|
|
1222
1421
|
onFlushError?(writes: readonly WriteRecord[], info: FlushInfo, error: unknown): void | Promise<void>;
|
|
1223
1422
|
}
|
|
1224
1423
|
|
|
1424
|
+
/** The dispatcher's view of its services' access policies: `dispatcher.access`. */
|
|
1425
|
+
interface DispatcherAccess {
|
|
1426
|
+
/**
|
|
1427
|
+
* The principal's level on each of `ids` of `service`, as an `{ entry }`
|
|
1428
|
+
* check counts it: the service's policy, or `Admin` on every row when the
|
|
1429
|
+
* principal holds a service-wide `Admin` grant and the service keeps
|
|
1430
|
+
* `adminBypass` on. A grant below `Admin` does not count. A row that is
|
|
1431
|
+
* not found has no level. One batched lookup per policy.
|
|
1432
|
+
*/
|
|
1433
|
+
levelsFor(service: AnyContract | string, principal: Principal, ids: readonly string[]): Promise<RowLevels>;
|
|
1434
|
+
/**
|
|
1435
|
+
* A filter for `db.<model>.findMany({ where })` matching exactly the rows of
|
|
1436
|
+
* `service` on which `levelsFor` gives the principal at least `level`:
|
|
1437
|
+
* `{}` (every row) for a service-wide `Admin` with `adminBypass`, or
|
|
1438
|
+
* `"none"` when no row matches.
|
|
1439
|
+
*/
|
|
1440
|
+
accessWhere(service: AnyContract | string, principal: Principal, level: AccessLevel): Promise<AccessFilter>;
|
|
1441
|
+
/**
|
|
1442
|
+
* Calls `listener` after each flush whose tracked writes may have changed
|
|
1443
|
+
* someone's access to a row (`AccessChange`), once the cache no longer
|
|
1444
|
+
* holds what they changed. A promise it returns is awaited before the
|
|
1445
|
+
* dispatcher's other flush sinks run. Returns a function that removes it.
|
|
1446
|
+
*/
|
|
1447
|
+
onAccessChanged(listener: AccessChangeListener): () => void;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* `ctx.access` (RFC 0003 section 4.3): `dispatcher.access` bound to the
|
|
1451
|
+
* call's caller, `ctx.principal`, with the grants that call runs with (its
|
|
1452
|
+
* socket's, or those `qd.caller` loaded). For a `custom(...)` form or a
|
|
1453
|
+
* handler that asks about a row its method's own form does not name: a
|
|
1454
|
+
* payment's loan, a parent named by an optional key. `service` is a
|
|
1455
|
+
* contract or a service name with an access policy on the dispatcher;
|
|
1456
|
+
* another is a `TypeError`. An anonymous caller (a `"public"` method called
|
|
1457
|
+
* without credentials) has no level on any row.
|
|
1458
|
+
*
|
|
1459
|
+
* @example
|
|
1460
|
+
* access: custom(async (ctx, input) => await ctx.access.can(loan, input.loanId, "Moderate")),
|
|
1461
|
+
*/
|
|
1462
|
+
interface ContextAccess {
|
|
1463
|
+
/**
|
|
1464
|
+
* The caller's level on each of `ids` of `service`, as an `{ entry }`
|
|
1465
|
+
* check counts it (`dispatcher.access.levelsFor` with `ctx.principal`): a
|
|
1466
|
+
* service-wide `Admin` grant gives `Admin` on every id while the service
|
|
1467
|
+
* keeps `adminBypass` on, and a row that is not found has no level. One
|
|
1468
|
+
* batched lookup per policy. An empty map for an anonymous caller.
|
|
1469
|
+
*/
|
|
1470
|
+
levelsFor(service: AnyContract | string, ids: readonly string[]): Promise<RowLevels>;
|
|
1471
|
+
/**
|
|
1472
|
+
* Whether the caller's level on row `id` of `service` is at least `level`,
|
|
1473
|
+
* as `{ entry: level }` decides it on that row: `false` for a row that is
|
|
1474
|
+
* not found (but for a service-wide `Admin` grant, which reaches every id
|
|
1475
|
+
* without reading it, as for `{ entry }`) and for an anonymous caller.
|
|
1476
|
+
*/
|
|
1477
|
+
can(service: AnyContract | string, id: string, level: AccessLevel): Promise<boolean>;
|
|
1478
|
+
/**
|
|
1479
|
+
* A filter for `db.<model>.findMany({ where })` matching exactly the rows
|
|
1480
|
+
* of `service` on which `can` gives the caller `level`, for a hand-written
|
|
1481
|
+
* list (`dispatcher.access.accessWhere` with `ctx.principal`): `{}` for a
|
|
1482
|
+
* service-wide `Admin` with `adminBypass`, `"none"` when no row matches
|
|
1483
|
+
* and for an anonymous caller.
|
|
1484
|
+
*/
|
|
1485
|
+
where(service: AnyContract | string, level: AccessLevel): Promise<AccessFilter>;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1225
1488
|
/** Options of one in-process call. */
|
|
1226
1489
|
interface CallOptions {
|
|
1227
1490
|
/** Cancels the call (queries only), like a client's `qd:cancel`. */
|
|
@@ -1248,4 +1511,4 @@ type CallerFor<T extends QuickdrawTypes> = T extends {
|
|
|
1248
1511
|
readonly contracts: infer Contracts extends ContractMap;
|
|
1249
1512
|
} ? Caller<Contracts[keyof Contracts]> : Caller<AnyContract>;
|
|
1250
1513
|
|
|
1251
|
-
export { type
|
|
1514
|
+
export { type AffectsLink as $, type AnyService as A, type PublicAccess as B, type CountArgs as C, DevWarningError as D, type HandlerArgs as E, type FindManyArgs as F, type CustomAccess as G, type HostLeaveHandler as H, type ShareMode as I, type ChannelOptions as J, type StreamOptions as K, type ChannelsRequired as L, type ModelName as M, type BaseContext as N, type CallerFor as O, type Presence as P, type AccessPolicy as Q, type RunContext as R, type StorageAdapter as S, type AnyAccessPolicy as T, type UnitOfWorkFactory as U, type PolicyColumns as V, type WriteRecord as W, type PolicyForeign as X, type PolicyTools as Y, type AccessFilter as Z, type RowAccess as _, type StorageRow as a, type ProjectedOutput as a0, type Projection as a1, type TierGroup as a2, type Tiers as a3, DEFAULT_BULK_THRESHOLD as a4, type CollectionScope as a5, type ServiceCollection as a6, type ContextAccess as a7, type ContextRooms as a8, type ContextServices as a9, type ParentLink as aA, type PolicyKind as aB, type PolicyReads as aC, type RowLevel as aD, type RowLevels as aE, type AccessChange as aF, type AccessChangeListener as aG, type AccessForm as aH, type AccessRequest as aI, type AuthenticatedAccess as aJ, type EntryAccess as aK, type IdKeyOf as aL, type IdSelector as aM, type ScopeAccess as aN, type ServiceAccess as aO, ANY_FIELD as aP, type UnitOfWork as aQ, type UnitOfWorkScope as aR, type LinkInterest as aS, formatDevWarning as aT, N_PLUS_ONE_STATEMENTS as aU, type TouchOptions as aa, type ChannelAccess as ab, type ChannelContext as ac, type ChannelHandler as ad, type ChannelImplementation as ae, type ChannelRoomOf as af, type HostLeave as ag, type RoomLeave as ah, type RoomLeaveReason as ai, type RoomLeft as aj, type RoomTarget as ak, type ServiceChannel as al, type ServiceStream as am, type StreamImplementation as an, type StreamPushArgs as ao, type StreamPushManyArgs as ap, type StreamSeed as aq, type StreamSeedContext as ar, type StreamSubscribersArgs as as, type StreamValidation as at, type CallOptions as au, type MethodCaller as av, type ServiceCaller as aw, type ForeignColumns as ax, type ForeignColumnsOf as ay, type MembershipRead as az, type StorageWhere as b, type StatementCount as c, type Caller as d, type FlushSink as e, type FlushInfo as f, type ServiceMethod as g, type Service as h, type DevWarning as i, type DevWarningKind as j, type RunOptions as k, type DispatcherAccess as l, type ServerRooms as m, type StreamHandle as n, type AnyContext as o, type AccessEngine as p, type AppRooms as q, type StreamSubscriptions as r, storageOf as s, type RoomLeaveHandler as t, type PolicyFor as u, type RowForms as v, type AccessFor as w, type HandlerContext as x, type ModelColumn as y, type WatchAccess as z };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_PEER_CANDIDATE_LENGTH,
|
|
3
|
+
MAX_PEER_SIGNAL_LENGTH,
|
|
4
|
+
MAX_SCOPE_LENGTH
|
|
5
|
+
} from "./chunk-X4ZU4QH5.js";
|
|
6
|
+
import {
|
|
7
|
+
isRecord
|
|
8
|
+
} from "./chunk-QSU47XEX.js";
|
|
9
|
+
|
|
10
|
+
// src/protocol/peer.ts
|
|
11
|
+
var MAX_SOCKET_ID_LENGTH = 64;
|
|
12
|
+
function isText(value) {
|
|
13
|
+
return typeof value === "string" && value.length <= MAX_PEER_SIGNAL_LENGTH;
|
|
14
|
+
}
|
|
15
|
+
function isCandidateText(value) {
|
|
16
|
+
return typeof value === "string" && value.length <= MAX_PEER_CANDIDATE_LENGTH;
|
|
17
|
+
}
|
|
18
|
+
function isRoomName(value) {
|
|
19
|
+
return typeof value === "string" && value !== "" && value.length <= MAX_SCOPE_LENGTH;
|
|
20
|
+
}
|
|
21
|
+
function isSocketId(value) {
|
|
22
|
+
return typeof value === "string" && value !== "" && value.length <= MAX_SOCKET_ID_LENGTH;
|
|
23
|
+
}
|
|
24
|
+
function isPeerLabel(value) {
|
|
25
|
+
return value === "rel" || value === "fast";
|
|
26
|
+
}
|
|
27
|
+
function nullable(value, check) {
|
|
28
|
+
return value === void 0 || value === null || check(value);
|
|
29
|
+
}
|
|
30
|
+
function isLineIndex(value) {
|
|
31
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
32
|
+
}
|
|
33
|
+
function readDescription(value) {
|
|
34
|
+
if (!isRecord(value) || value.type !== "offer" && value.type !== "answer") {
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
return isText(value.sdp) ? { type: value.type, sdp: value.sdp } : void 0;
|
|
38
|
+
}
|
|
39
|
+
function readCandidate(value) {
|
|
40
|
+
if (!isRecord(value) || !isCandidateText(value.candidate)) {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
const { sdpMid, sdpMLineIndex, usernameFragment } = value;
|
|
44
|
+
if (!nullable(sdpMid, isCandidateText) || !nullable(sdpMLineIndex, isLineIndex) || !nullable(usernameFragment, isCandidateText)) {
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
candidate: value.candidate,
|
|
49
|
+
...sdpMid === void 0 ? {} : { sdpMid },
|
|
50
|
+
...sdpMLineIndex === void 0 ? {} : { sdpMLineIndex },
|
|
51
|
+
...usernameFragment === void 0 ? {} : { usernameFragment }
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function readSignal(value) {
|
|
55
|
+
if (!isRecord(value)) {
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
const { sdp, ice, relay, bye } = value;
|
|
59
|
+
if ([sdp, ice, relay, bye].filter((member) => member !== void 0).length !== 1) {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
if (sdp !== void 0) {
|
|
63
|
+
const description = readDescription(sdp);
|
|
64
|
+
return description === void 0 ? void 0 : { sdp: description };
|
|
65
|
+
}
|
|
66
|
+
if (ice !== void 0) {
|
|
67
|
+
const candidate = readCandidate(ice);
|
|
68
|
+
return candidate === void 0 ? void 0 : { ice: candidate };
|
|
69
|
+
}
|
|
70
|
+
if (relay === true) {
|
|
71
|
+
return { relay: true };
|
|
72
|
+
}
|
|
73
|
+
return bye === true ? { bye: true } : void 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
isRoomName,
|
|
78
|
+
isSocketId,
|
|
79
|
+
isPeerLabel,
|
|
80
|
+
readSignal
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=chunk-2FDSKLBO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/protocol/peer.ts"],"sourcesContent":["// Reading peer link signals (RFC 0003 section 12.5), for the server that\n// forwards them and the client that applies them. A signal is copied member\n// by member, so what goes on is only what was read: a session description\n// of at most `MAX_PEER_SIGNAL_LENGTH` characters, an ICE candidate whose\n// strings hold at most `MAX_PEER_CANDIDATE_LENGTH`, `relay` or `bye`.\n// Internal: neither the package root nor `./client` exports these.\n\nimport type { PeerCandidate, PeerDescription, PeerLabel, PeerSignal } from \"./envelope\";\nimport { isRecord } from \"./guards\";\nimport { MAX_PEER_CANDIDATE_LENGTH, MAX_PEER_SIGNAL_LENGTH, MAX_SCOPE_LENGTH } from \"./version\";\n\n/** The longest socket id a peer frame may name. Socket.IO's are 20 characters. */\nexport const MAX_SOCKET_ID_LENGTH = 64;\n\n/** True for a string of at most `MAX_PEER_SIGNAL_LENGTH` characters, empty included. */\nfunction isText(value: unknown): value is string {\n return typeof value === \"string\" && value.length <= MAX_PEER_SIGNAL_LENGTH;\n}\n\n/** True for a string of at most `MAX_PEER_CANDIDATE_LENGTH` characters, empty included. */\nfunction isCandidateText(value: unknown): value is string {\n return typeof value === \"string\" && value.length <= MAX_PEER_CANDIDATE_LENGTH;\n}\n\n/** True for what may name an app room: 1 to `MAX_SCOPE_LENGTH` characters. */\nexport function isRoomName(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\" && value.length <= MAX_SCOPE_LENGTH;\n}\n\n/** True for what may name a socket: 1 to `MAX_SOCKET_ID_LENGTH` characters. */\nexport function isSocketId(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\" && value.length <= MAX_SOCKET_ID_LENGTH;\n}\n\n/** True for `rel` and `fast`. */\nexport function isPeerLabel(value: unknown): value is PeerLabel {\n return value === \"rel\" || value === \"fast\";\n}\n\n/** True when `value` is absent, `null`, or passes `check`. */\nfunction nullable(value: unknown, check: (inner: unknown) => boolean): boolean {\n return value === undefined || value === null || check(value);\n}\n\nfunction isLineIndex(value: unknown): boolean {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction readDescription(value: unknown): PeerDescription | undefined {\n if (!isRecord(value) || (value.type !== \"offer\" && value.type !== \"answer\")) {\n return undefined;\n }\n return isText(value.sdp) ? { type: value.type, sdp: value.sdp } : undefined;\n}\n\nfunction readCandidate(value: unknown): PeerCandidate | undefined {\n if (!isRecord(value) || !isCandidateText(value.candidate)) {\n return undefined;\n }\n const { sdpMid, sdpMLineIndex, usernameFragment } = value;\n if (\n !nullable(sdpMid, isCandidateText) ||\n !nullable(sdpMLineIndex, isLineIndex) ||\n !nullable(usernameFragment, isCandidateText)\n ) {\n return undefined;\n }\n return {\n candidate: value.candidate,\n ...(sdpMid === undefined ? {} : { sdpMid: sdpMid as string | null }),\n ...(sdpMLineIndex === undefined ? {} : { sdpMLineIndex: sdpMLineIndex as number | null }),\n ...(usernameFragment === undefined\n ? {}\n : { usernameFragment: usernameFragment as string | null }),\n };\n}\n\n/**\n * The signal `value` holds, copied, or `undefined` unless it holds exactly\n * one of `sdp`, `ice`, `relay: true` and `bye: true`, well formed. Fields\n * it does not know are left behind.\n */\nexport function readSignal(value: unknown): PeerSignal | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n const { sdp, ice, relay, bye } = value;\n if ([sdp, ice, relay, bye].filter((member) => member !== undefined).length !== 1) {\n return undefined;\n }\n if (sdp !== undefined) {\n const description = readDescription(sdp);\n return description === undefined ? undefined : { sdp: description };\n }\n if (ice !== undefined) {\n const candidate = readCandidate(ice);\n return candidate === undefined ? undefined : { ice: candidate };\n }\n if (relay === true) {\n return { relay: true };\n }\n return bye === true ? { bye: true } : undefined;\n}\n"],"mappings":";;;;;;;;;;AAYO,IAAM,uBAAuB;AAGpC,SAAS,OAAO,OAAiC;AAC/C,SAAO,OAAO,UAAU,YAAY,MAAM,UAAU;AACtD;AAGA,SAAS,gBAAgB,OAAiC;AACxD,SAAO,OAAO,UAAU,YAAY,MAAM,UAAU;AACtD;AAGO,SAAS,WAAW,OAAiC;AAC1D,SAAO,OAAO,UAAU,YAAY,UAAU,MAAM,MAAM,UAAU;AACtE;AAGO,SAAS,WAAW,OAAiC;AAC1D,SAAO,OAAO,UAAU,YAAY,UAAU,MAAM,MAAM,UAAU;AACtE;AAGO,SAAS,YAAY,OAAoC;AAC9D,SAAO,UAAU,SAAS,UAAU;AACtC;AAGA,SAAS,SAAS,OAAgB,OAA6C;AAC7E,SAAO,UAAU,UAAa,UAAU,QAAQ,MAAM,KAAK;AAC7D;AAEA,SAAS,YAAY,OAAyB;AAC5C,SAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,SAAS;AAC9E;AAEA,SAAS,gBAAgB,OAA6C;AACpE,MAAI,CAAC,SAAS,KAAK,KAAM,MAAM,SAAS,WAAW,MAAM,SAAS,UAAW;AAC3E,WAAO;AAAA,EACT;AACA,SAAO,OAAO,MAAM,GAAG,IAAI,EAAE,MAAM,MAAM,MAAM,KAAK,MAAM,IAAI,IAAI;AACpE;AAEA,SAAS,cAAc,OAA2C;AAChE,MAAI,CAAC,SAAS,KAAK,KAAK,CAAC,gBAAgB,MAAM,SAAS,GAAG;AACzD,WAAO;AAAA,EACT;AACA,QAAM,EAAE,QAAQ,eAAe,iBAAiB,IAAI;AACpD,MACE,CAAC,SAAS,QAAQ,eAAe,KACjC,CAAC,SAAS,eAAe,WAAW,KACpC,CAAC,SAAS,kBAAkB,eAAe,GAC3C;AACA,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,WAAW,MAAM;AAAA,IACjB,GAAI,WAAW,SAAY,CAAC,IAAI,EAAE,OAAgC;AAAA,IAClE,GAAI,kBAAkB,SAAY,CAAC,IAAI,EAAE,cAA8C;AAAA,IACvF,GAAI,qBAAqB,SACrB,CAAC,IACD,EAAE,iBAAoD;AAAA,EAC5D;AACF;AAOO,SAAS,WAAW,OAAwC;AACjE,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,EAAE,KAAK,KAAK,OAAO,IAAI,IAAI;AACjC,MAAI,CAAC,KAAK,KAAK,OAAO,GAAG,EAAE,OAAO,CAAC,WAAW,WAAW,MAAS,EAAE,WAAW,GAAG;AAChF,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,QAAW;AACrB,UAAM,cAAc,gBAAgB,GAAG;AACvC,WAAO,gBAAgB,SAAY,SAAY,EAAE,KAAK,YAAY;AAAA,EACpE;AACA,MAAI,QAAQ,QAAW;AACrB,UAAM,YAAY,cAAc,GAAG;AACnC,WAAO,cAAc,SAAY,SAAY,EAAE,KAAK,UAAU;AAAA,EAChE;AACA,MAAI,UAAU,MAAM;AAClB,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AACA,SAAO,QAAQ,OAAO,EAAE,KAAK,KAAK,IAAI;AACxC;","names":[]}
|