@protobase/client 0.23.3 → 0.23.4

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/client.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { FilterInput, ListParams, MapParams } from "./query-string.js";
2
2
  import { ClientOptions } from "./transport.js";
3
3
  import { UploadProgress } from "./upload-request.js";
4
+ import { LiveHandlers } from "./live.js";
4
5
  import { Aggregate, AggregateParams, BatchOp, BatchResult, CommentPage, Facet, FormChallenge, FormProof, Histogram, HistoryPage, ListPage, MapResult, MentionableUser, MetaResult, PublicForm, RecordComment, RecordOf, ResourceRef, Series, SeriesParams, Stored } from "./types.js";
5
6
  import { KeyValue } from "@protobase/schema";
6
7
  //#region src/client.d.ts
@@ -93,6 +94,7 @@ export declare const createClient: (options?: ClientOptions) => {
93
94
  form: (name: string) => Promise<PublicForm>;
94
95
  formChallenge: (name: string) => Promise<FormChallenge>;
95
96
  submitForm: (name: string, values: Record<string, unknown>, proof: FormProof) => Promise<void>;
97
+ live: (handlers: LiveHandlers) => () => void;
96
98
  };
97
99
  export type Client = ReturnType<typeof createClient>;
98
100
  //#endregion
package/dist/client.js CHANGED
@@ -2,6 +2,7 @@ import { filterText, labelEntries, listWire, photosWire, queryString } from "./q
2
2
  import { originOf, photosWithOrigin, withFileOrigin } from "./file-urls.js";
3
3
  import { createTransport } from "./transport.js";
4
4
  import { fetchWithProgress } from "./upload-request.js";
5
+ import { followLive } from "./live.js";
5
6
  import { encodeKey } from "@protobase/schema/internal";
6
7
  //#region src/client.ts
7
8
  const nameOf = (resource) => typeof resource === "string" ? resource : resource.toModel().name;
@@ -404,6 +405,11 @@ const createClient = (options = {}) => {
404
405
  system: true
405
406
  });
406
407
  };
408
+ /**
409
+ * Hears which records of the `.live()` resources the caller may read change, as they do, until the returned function
410
+ * is called (`GET /api/live`); it connects again by itself.
411
+ */
412
+ const live = (handlers) => followLive(send, handlers);
407
413
  return {
408
414
  list,
409
415
  search,
@@ -430,7 +436,8 @@ const createClient = (options = {}) => {
430
436
  upload,
431
437
  form,
432
438
  formChallenge,
433
- submitForm
439
+ submitForm,
440
+ live
434
441
  };
435
442
  };
436
443
  //#endregion
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { FilterInput, ListParams, MapParams, filterText, listWire } from "./query-string.js";
2
2
  import { ClientOptions } from "./transport.js";
3
3
  import { UploadProgress } from "./upload-request.js";
4
+ import { LiveChange, LiveHandlers } from "./live.js";
4
5
  import { Aggregate, AggregateGroup, AggregateParams, AggregateSplitTotal, BatchOp, BatchRef, BatchResult, CommentPage, CommentPerson, Facet, FormChallenge, FormProof, Histogram, HistogramBucket, HistoryChange, HistoryPage, ListPage, MapBounds, MapBucket, MapLayerModel, MapResult, MentionableUser, Meta, MetaResult, PluginRef, PublicForm, RecordComment, RecordOf, RecordPermissions, RecordPhotos, RelatedLabels, ResourcePermissions, ResourceRef, Series, SeriesParams, SeriesPoint, SourceRef, Stored, StoredFile, StoredFileVariant } from "./types.js";
5
6
  import { Client, InvokeOptions, UploadOptions, UploadResult, createClient } from "./client.js";
6
7
  import { ApiError, PreconditionFailedError, ProblemDetails, ProblemError, isApiError } from "./problem.js";
@@ -19,4 +20,4 @@ import { AuthSession, AuthSessionOptions, createAuthSession, tokenExpiry } from
19
20
  import { createStaticSession } from "./static-session.js";
20
21
  import { AssistantClient, AssistantClientOptions, AssistantConnection, createAssistantClient } from "./assistant.js";
21
22
  import { RuntimeClient, RuntimeClientOptions, RuntimeLastUpdate, RuntimePolicy, RuntimeStatus, RuntimeUpdateStage, createRuntimeClient } from "./runtime.js";
22
- export { type AccountSecurity, type AccountSignIn, type ActiveRole, type Aggregate, type AggregateGroup, type AggregateParams, type AggregateSplitTotal, ApiError, type AppUser, type AssistantClient, type AssistantClientOptions, type AssistantConnection, AuthError, type AuthSession, type AuthSessionOptions, type AuthUser, type AuthenticatorSetup, type BatchOp, type BatchRef, type BatchResult, type Client, type ClientOptions, type CommentPage, type CommentPerson, type CurrentOrganization, type Facet, type FilterInput, type FormChallenge, type FormProof, type Histogram, type HistogramBucket, type HistoryChange, type HistoryPage, type InvitationPreview, type InvitationSent, type InvokeOptions, type ListPage, type ListParams, type MapBounds, type MapBucket, type MapLayerModel, type MapParams, type MapResult, type MentionableUser, type Meta, type MetaResult, type Organization, type OrganizationInvitation, type OrganizationMember, type OrganizationRole, type OrganizationsClient, type OrganizationsFound, type Passkey, type PlatformSignIn, type PluginRef, PreconditionFailedError, type ProblemDetails, type ProblemError, type PublicForm, type RecordComment, type RecordOf, type RecordPermissions, type RecordPhotos, type RelatedLabels, type RequiredSetup, type ResourcePermissions, type ResourceRef, type RoleLabel, type RoleOption, type RolesClient, type RuntimeClient, type RuntimeClientOptions, type RuntimeLastUpdate, type RuntimePolicy, type RuntimeStatus, type RuntimeUpdateStage, type SamlSignIn, type Series, type SeriesParams, type SeriesPoint, type SetupStatus, type SignInMethod, type SignInPolicy, type SignInPolicyClient, type SignInPolicyState, type SignInResult, type SignInRule, type SourceMeta, type SourceRef, type StaffAccessRule, type StaffSignIn, type StaffSignInClient, type Stored, type StoredFile, type StoredFileVariant, type TwoFactorMethod, type UploadOptions, type UploadProgress, type UploadResult, type UserInvitation, type UserInvitationSent, type UsersClient, type UsersFound, createAssistantClient, createAuthSession, createClient, createRuntimeClient, createStaticSession, fileUrl, filterText, isApiError, listWire, routedClient, sourceMeta, tokenExpiry };
23
+ export { type AccountSecurity, type AccountSignIn, type ActiveRole, type Aggregate, type AggregateGroup, type AggregateParams, type AggregateSplitTotal, ApiError, type AppUser, type AssistantClient, type AssistantClientOptions, type AssistantConnection, AuthError, type AuthSession, type AuthSessionOptions, type AuthUser, type AuthenticatorSetup, type BatchOp, type BatchRef, type BatchResult, type Client, type ClientOptions, type CommentPage, type CommentPerson, type CurrentOrganization, type Facet, type FilterInput, type FormChallenge, type FormProof, type Histogram, type HistogramBucket, type HistoryChange, type HistoryPage, type InvitationPreview, type InvitationSent, type InvokeOptions, type ListPage, type ListParams, type LiveChange, type LiveHandlers, type MapBounds, type MapBucket, type MapLayerModel, type MapParams, type MapResult, type MentionableUser, type Meta, type MetaResult, type Organization, type OrganizationInvitation, type OrganizationMember, type OrganizationRole, type OrganizationsClient, type OrganizationsFound, type Passkey, type PlatformSignIn, type PluginRef, PreconditionFailedError, type ProblemDetails, type ProblemError, type PublicForm, type RecordComment, type RecordOf, type RecordPermissions, type RecordPhotos, type RelatedLabels, type RequiredSetup, type ResourcePermissions, type ResourceRef, type RoleLabel, type RoleOption, type RolesClient, type RuntimeClient, type RuntimeClientOptions, type RuntimeLastUpdate, type RuntimePolicy, type RuntimeStatus, type RuntimeUpdateStage, type SamlSignIn, type Series, type SeriesParams, type SeriesPoint, type SetupStatus, type SignInMethod, type SignInPolicy, type SignInPolicyClient, type SignInPolicyState, type SignInResult, type SignInRule, type SourceMeta, type SourceRef, type StaffAccessRule, type StaffSignIn, type StaffSignInClient, type Stored, type StoredFile, type StoredFileVariant, type TwoFactorMethod, type UploadOptions, type UploadProgress, type UploadResult, type UserInvitation, type UserInvitationSent, type UsersClient, type UsersFound, createAssistantClient, createAuthSession, createClient, createRuntimeClient, createStaticSession, fileUrl, filterText, isApiError, listWire, routedClient, sourceMeta, tokenExpiry };
package/dist/live.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ //#region src/live.d.ts
2
+ /** A record of a `.live()` resource that changed: its key, never its values. */
3
+ export type LiveChange = {
4
+ resource: string;
5
+ key: string;
6
+ operation: 'create' | 'update' | 'delete' | 'undelete';
7
+ };
8
+ export type LiveHandlers = {
9
+ /** The records that changed, in commit order, of the live resources the caller may read. */
10
+ onChanges: (changes: LiveChange[]) => void;
11
+ /** Changes went by while the connection was down for longer than the server keeps them: read everything shown again. */
12
+ onResync: () => void;
13
+ /** The stream failed, with why, or ended (`undefined`); it is followed again after a pause. */
14
+ onRetry?: (error: unknown) => void;
15
+ };
16
+ //#endregion
package/dist/live.js ADDED
@@ -0,0 +1,50 @@
1
+ import { queryString } from "./query-string.js";
2
+ import { readEventStream } from "@protobase/schema/internal";
3
+ //#region src/live.ts
4
+ const backoff = (attempt) => Math.min(3e4, 1e3 * 2 ** attempt);
5
+ const pause = (ms, signal) => new Promise((resolve) => {
6
+ const timer = setTimeout(resolve, ms);
7
+ signal.addEventListener("abort", () => {
8
+ clearTimeout(timer);
9
+ resolve();
10
+ }, { once: true });
11
+ });
12
+ /**
13
+ * Follows `GET /api/live` until the returned function is called: a stream that ends or fails is followed again, after
14
+ * a pause that grows with each failure, from the last event it had, so nothing in between is missed.
15
+ */
16
+ const followLive = (send, handlers, retryDelay = backoff) => {
17
+ const controller = new AbortController();
18
+ const { signal } = controller;
19
+ let cursor;
20
+ let failures = 0;
21
+ const onEvent = (event) => {
22
+ failures = 0;
23
+ cursor = event.cursor;
24
+ if (event.type === "resync") handlers.onResync();
25
+ if (event.type === "changes") handlers.onChanges(event.changes);
26
+ };
27
+ const follow = async () => {
28
+ const response = await send({
29
+ method: "GET",
30
+ path: "/live",
31
+ query: queryString({ after: cursor }),
32
+ headers: { accept: "text/event-stream" },
33
+ system: true,
34
+ signal
35
+ });
36
+ if (!response.body) throw new Error("The server answered the live stream without a body");
37
+ await readEventStream(response.body, (data) => onEvent(JSON.parse(data)), signal);
38
+ };
39
+ (async () => {
40
+ while (!signal.aborted) {
41
+ const error = await follow().then(() => void 0, (reason) => reason);
42
+ if (signal.aborted) return;
43
+ handlers.onRetry?.(error);
44
+ await pause(retryDelay(failures++), signal);
45
+ }
46
+ })();
47
+ return () => controller.abort();
48
+ };
49
+ //#endregion
50
+ export { followLive };
@@ -2,8 +2,8 @@ import { Client } from "./client.js";
2
2
  //#region src/routed-client.d.ts
3
3
  /**
4
4
  * One client over the app's own API and the sources it draws resources from: a call about a resource goes to the API
5
- * that serves it (`sourceOf` gives a source's client, or `undefined` for the app's own), and `meta`, `invoke` and the
6
- * public forms go to the app's own. A batch goes to the one API all its writes are for; one that writes to two cannot be atomic, so it is
5
+ * that serves it (`sourceOf` gives a source's client, or `undefined` for the app's own), and `meta`, `invoke`, the
6
+ * public forms and `live` go to the app's own. A batch goes to the one API all its writes are for; one that writes to two cannot be atomic, so it is
7
7
  * refused before anything is sent.
8
8
  */
9
9
  export declare const routedClient: (own: Client, sourceOf: (resource: string) => Client | undefined) => Client;
@@ -2,8 +2,8 @@
2
2
  const nameOf = (resource) => typeof resource === "string" ? resource : resource.toModel().name;
3
3
  /**
4
4
  * One client over the app's own API and the sources it draws resources from: a call about a resource goes to the API
5
- * that serves it (`sourceOf` gives a source's client, or `undefined` for the app's own), and `meta`, `invoke` and the
6
- * public forms go to the app's own. A batch goes to the one API all its writes are for; one that writes to two cannot be atomic, so it is
5
+ * that serves it (`sourceOf` gives a source's client, or `undefined` for the app's own), and `meta`, `invoke`, the
6
+ * public forms and `live` go to the app's own. A batch goes to the one API all its writes are for; one that writes to two cannot be atomic, so it is
7
7
  * refused before anything is sent.
8
8
  */
9
9
  const routedClient = (own, sourceOf) => {
@@ -38,7 +38,8 @@ const routedClient = (own, sourceOf) => {
38
38
  upload: (resource, field, file, options) => to(resource).upload(resource, field, file, options),
39
39
  form: (name) => own.form(name),
40
40
  formChallenge: (name) => own.formChallenge(name),
41
- submitForm: (name, values, proof) => own.submitForm(name, values, proof)
41
+ submitForm: (name, values, proof) => own.submitForm(name, values, proof),
42
+ live: (handlers) => own.live(handlers)
42
43
  };
43
44
  };
44
45
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/client",
3
- "version": "0.23.3",
3
+ "version": "0.23.4",
4
4
  "description": "Typed fetch client for the Protobase REST API.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -30,8 +30,8 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@better-auth/passkey": "^1.7.7",
33
- "@protobase/layout": "0.23.3",
34
- "@protobase/schema": "0.23.3",
33
+ "@protobase/layout": "0.23.4",
34
+ "@protobase/schema": "0.23.4",
35
35
  "@simplewebauthn/browser": "^13.3.0",
36
36
  "better-auth": "^1.7.7"
37
37
  }