@protobase/client 0.23.2 → 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 +2 -0
- package/dist/client.js +8 -1
- package/dist/index.d.ts +2 -1
- package/dist/live.d.ts +16 -0
- package/dist/live.js +50 -0
- package/dist/routed-client.d.ts +2 -2
- package/dist/routed-client.js +4 -3
- package/package.json +3 -3
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 };
|
package/dist/routed-client.d.ts
CHANGED
|
@@ -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
|
|
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;
|
package/dist/routed-client.js
CHANGED
|
@@ -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
|
|
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
|
+
"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.
|
|
34
|
-
"@protobase/schema": "0.23.
|
|
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
|
}
|