@protobase/client 0.45.19 → 0.45.21

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,5 +1,7 @@
1
- import { FilterInput, ListParams, MapParams } from "./query-string.js";
2
1
  import { ClientOptions } from "./transport.js";
2
+ import { RegisteredDevice, WebDeviceRegistration } from "./devices.js";
3
+ import { InboxPage } from "./notification-history.js";
4
+ import { FilterInput, ListParams, MapParams } from "./query-string.js";
3
5
  import { UploadProgress } from "./upload-request.js";
4
6
  import { LiveHandlers } from "./live.js";
5
7
  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";
@@ -95,6 +97,19 @@ export declare const createClient: (options?: ClientOptions) => {
95
97
  formChallenge: (name: string) => Promise<FormChallenge>;
96
98
  submitForm: (name: string, values: Record<string, unknown>, proof: FormProof) => Promise<void>;
97
99
  live: (handlers: LiveHandlers) => () => void;
100
+ devices: {
101
+ register: (device: WebDeviceRegistration) => Promise<RegisteredDevice>;
102
+ remove: (id: string) => Promise<void>;
103
+ };
104
+ notifications: {
105
+ list: ({ cursor, limit }?: {
106
+ cursor?: string;
107
+ limit?: number;
108
+ }) => Promise<InboxPage>;
109
+ unread: () => Promise<number>;
110
+ markRead: (id: string) => Promise<void>;
111
+ markAllRead: (upTo: string) => Promise<void>;
112
+ };
98
113
  };
99
114
  export type Client = ReturnType<typeof createClient>;
100
115
  //#endregion
package/dist/client.js CHANGED
@@ -3,6 +3,8 @@ import { originOf, photosWithOrigin, withFileOrigin } from "./file-urls.js";
3
3
  import { createTransport } from "./transport.js";
4
4
  import { fetchWithProgress } from "./upload-request.js";
5
5
  import { followLive } from "./live.js";
6
+ import { devicesClient } from "./devices.js";
7
+ import { notificationHistoryClient } from "./notification-history.js";
6
8
  import { encodeKey } from "@protobase/schema/internal";
7
9
  //#region src/client.ts
8
10
  const nameOf = (resource) => typeof resource === "string" ? resource : resource.toModel().name;
@@ -438,7 +440,9 @@ const createClient = (options = {}) => {
438
440
  form,
439
441
  formChallenge,
440
442
  submitForm,
441
- live
443
+ live,
444
+ devices: devicesClient(send),
445
+ notifications: notificationHistoryClient(send)
442
446
  };
443
447
  };
444
448
  //#endregion
@@ -0,0 +1,34 @@
1
+ import { Transport } from "./transport.js";
2
+ //#region src/devices.d.ts
3
+ /**
4
+ * A browser's push subscription as it registers it: its endpoint and keys, what it is, and the user whose click
5
+ * started the registration, which the server refuses with a 409 when someone else is signed in by then.
6
+ */
7
+ export type WebDeviceRegistration = {
8
+ kind: 'web';
9
+ endpoint: string;
10
+ keys: {
11
+ p256dh: string;
12
+ auth: string;
13
+ };
14
+ userAgent?: string;
15
+ userId?: string;
16
+ };
17
+ /** A device as the server answers it: never its token or keys. */
18
+ export type RegisteredDevice = {
19
+ id: string;
20
+ kind: 'web' | 'apns' | 'fcm';
21
+ createdAt: string;
22
+ lastSeenAt: string;
23
+ };
24
+ /**
25
+ * The signed-in user's own devices (`/api/notifications/devices`): `register` adds or refreshes the device the request
26
+ * comes from, and rejects with an `ApiError` of slug `device-held` when another user holds the browser's endpoint, or
27
+ * `other-user` when the user it names is not the one signed in; `remove` removes one by its id.
28
+ */
29
+ export declare const devicesClient: (send: Transport) => {
30
+ register: (device: WebDeviceRegistration) => Promise<RegisteredDevice>;
31
+ remove: (id: string) => Promise<void>;
32
+ };
33
+ export type DevicesClient = ReturnType<typeof devicesClient>;
34
+ //#endregion
@@ -0,0 +1,24 @@
1
+ import { devicesPath } from "@protobase/schema/internal";
2
+ //#region src/devices.ts
3
+ /**
4
+ * The signed-in user's own devices (`/api/notifications/devices`): `register` adds or refreshes the device the request
5
+ * comes from, and rejects with an `ApiError` of slug `device-held` when another user holds the browser's endpoint, or
6
+ * `other-user` when the user it names is not the one signed in; `remove` removes one by its id.
7
+ */
8
+ const devicesClient = (send) => ({
9
+ register: async (device) => (await send({
10
+ method: "POST",
11
+ path: devicesPath(""),
12
+ system: true,
13
+ body: device
14
+ })).json(),
15
+ remove: async (id) => {
16
+ await send({
17
+ method: "DELETE",
18
+ path: devicesPath("", id),
19
+ system: true
20
+ });
21
+ }
22
+ });
23
+ //#endregion
24
+ export { devicesClient };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { FilterInput, ListParams, MapParams, filterText, listWire } from "./query-string.js";
2
1
  import { ClientOptions } from "./transport.js";
2
+ import { DevicesClient, RegisteredDevice, WebDeviceRegistration } from "./devices.js";
3
+ import { InboxNotification, InboxPage, NotificationHistoryClient } from "./notification-history.js";
4
+ import { FilterInput, ListParams, MapParams, filterText, listWire } from "./query-string.js";
3
5
  import { UploadProgress } from "./upload-request.js";
4
6
  import { LiveChange, LiveHandlers } from "./live.js";
5
7
  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";
@@ -21,4 +23,4 @@ import { AuthSession, AuthSessionOptions, createAuthSession, tokenExpiry } from
21
23
  import { createStaticSession } from "./static-session.js";
22
24
  import { AssistantClient, AssistantClientOptions, AssistantConnection, createAssistantClient } from "./assistant.js";
23
25
  import { RuntimeClient, RuntimeClientOptions, RuntimeLastUpdate, RuntimePolicy, RuntimeStatus, RuntimeUpdateStage, createRuntimeClient } from "./runtime.js";
24
- 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 ConnectClient, type ConnectedApp, type ConnectingClient, 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 };
26
+ 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 ConnectClient, type ConnectedApp, type ConnectingClient, type CurrentOrganization, type DevicesClient, type Facet, type FilterInput, type FormChallenge, type FormProof, type Histogram, type HistogramBucket, type HistoryChange, type HistoryPage, type InboxNotification, type InboxPage, 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 NotificationHistoryClient, 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 RegisteredDevice, 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, type WebDeviceRegistration, createAssistantClient, createAuthSession, createClient, createRuntimeClient, createStaticSession, fileUrl, filterText, isApiError, listWire, routedClient, sourceMeta, tokenExpiry };
@@ -0,0 +1,36 @@
1
+ import { Transport } from "./transport.js";
2
+ //#region src/notification-history.d.ts
3
+ /**
4
+ * One of the signed-in user's inbox notifications, as the server answers it: `title` and `body` are plain text, `url` a
5
+ * path on the app or an absolute URL on one of its origins, or `null`; `readAt` is `null` while it is unread.
6
+ */
7
+ export type InboxNotification = {
8
+ id: string;
9
+ title: string;
10
+ body: string | null;
11
+ url: string | null;
12
+ data: Record<string, unknown> | null;
13
+ createdAt: string;
14
+ readAt: string | null;
15
+ };
16
+ /** A page of the history, newest first; `next` is the cursor of the page after it, `null` on the last one. */
17
+ export type InboxPage = {
18
+ items: InboxNotification[];
19
+ next: string | null;
20
+ };
21
+ /**
22
+ * The signed-in user's own notification history (`/api/notifications/history`): `list` a page of it, `unread` how many
23
+ * are unread (at most 100), `markRead` one of them by its id, and `markAllRead` those created up to `upTo`, the
24
+ * `createdAt` of the newest one the user was shown.
25
+ */
26
+ export declare const notificationHistoryClient: (send: Transport) => {
27
+ list: ({ cursor, limit }?: {
28
+ cursor?: string;
29
+ limit?: number;
30
+ }) => Promise<InboxPage>;
31
+ unread: () => Promise<number>;
32
+ markRead: (id: string) => Promise<void>;
33
+ markAllRead: (upTo: string) => Promise<void>;
34
+ };
35
+ export type NotificationHistoryClient = ReturnType<typeof notificationHistoryClient>;
36
+ //#endregion
@@ -0,0 +1,45 @@
1
+ import { historyPath } from "@protobase/schema/internal";
2
+ //#region src/notification-history.ts
3
+ /**
4
+ * The signed-in user's own notification history (`/api/notifications/history`): `list` a page of it, `unread` how many
5
+ * are unread (at most 100), `markRead` one of them by its id, and `markAllRead` those created up to `upTo`, the
6
+ * `createdAt` of the newest one the user was shown.
7
+ */
8
+ const notificationHistoryClient = (send) => ({
9
+ list: async ({ cursor, limit } = {}) => {
10
+ const params = new URLSearchParams({
11
+ ...cursor !== void 0 && { cursor },
12
+ ...limit !== void 0 && { limit: String(limit) }
13
+ });
14
+ const query = params.size > 0 ? `?${params}` : "";
15
+ return (await send({
16
+ method: "GET",
17
+ path: historyPath(""),
18
+ query,
19
+ system: true
20
+ })).json();
21
+ },
22
+ unread: async () => (await (await send({
23
+ method: "GET",
24
+ path: historyPath("", "unread"),
25
+ system: true
26
+ })).json()).count,
27
+ markRead: async (id) => {
28
+ await send({
29
+ method: "POST",
30
+ path: historyPath("", { read: id }),
31
+ body: {},
32
+ system: true
33
+ });
34
+ },
35
+ markAllRead: async (upTo) => {
36
+ await send({
37
+ method: "POST",
38
+ path: historyPath("", "read"),
39
+ body: { upTo },
40
+ system: true
41
+ });
42
+ }
43
+ });
44
+ //#endregion
45
+ export { notificationHistoryClient };
@@ -3,7 +3,7 @@ import { Client } from "./client.js";
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
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
6
+ * public forms, `live` and the user's devices and notifications 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;
@@ -3,7 +3,7 @@ const nameOf = (resource) => typeof resource === "string" ? resource : resource.
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
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
6
+ * public forms, `live` and the user's devices and notifications 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) => {
@@ -39,7 +39,9 @@ const routedClient = (own, sourceOf) => {
39
39
  form: (name) => own.form(name),
40
40
  formChallenge: (name) => own.formChallenge(name),
41
41
  submitForm: (name, values, proof) => own.submitForm(name, values, proof),
42
- live: (handlers) => own.live(handlers)
42
+ live: (handlers) => own.live(handlers),
43
+ devices: own.devices,
44
+ notifications: own.notifications
43
45
  };
44
46
  };
45
47
  //#endregion
@@ -16,4 +16,22 @@ export type ClientOptions = {
16
16
  /** Called when a request is still 401 after the retry: the session is over. */
17
17
  onUnauthenticated?: () => void;
18
18
  };
19
+ export type Request = {
20
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
21
+ path: string;
22
+ query?: string;
23
+ body?: unknown;
24
+ headers?: Record<string, string>;
25
+ /** Statuses other than 2xx that are an answer rather than an error. */
26
+ accept?: number[];
27
+ /** A system endpoint such as `/meta`: it lives beside the API (`/api/meta`), not inside it (`/api/v1`). */
28
+ system?: boolean;
29
+ /** Sent as it is instead of a JSON `body`, such as a file. */
30
+ raw?: Blob;
31
+ /** Sends this request instead of the client's fetch, such as one that reports upload progress. */
32
+ via?: typeof fetch;
33
+ signal?: AbortSignal;
34
+ };
35
+ export declare const createTransport: (options?: ClientOptions) => ({ method, path, query, body, headers, accept, system, raw, via, signal }: Request) => Promise<Response>;
36
+ export type Transport = ReturnType<typeof createTransport>;
19
37
  //#endregion
package/dist/types.d.ts CHANGED
@@ -280,7 +280,8 @@ export type Meta = {
280
280
  layers?: MapLayerModel[];
281
281
  };
282
282
  notifications?: {
283
- webPushKey: string;
283
+ bell?: true;
284
+ webPushKey?: string;
284
285
  };
285
286
  locale?: 'en' | 'nl';
286
287
  files?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/client",
3
- "version": "0.45.19",
3
+ "version": "0.45.21",
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.12",
33
- "@protobase/layout": "0.45.19",
34
- "@protobase/schema": "0.45.19",
33
+ "@protobase/layout": "0.45.21",
34
+ "@protobase/schema": "0.45.21",
35
35
  "@simplewebauthn/browser": "^13.3.0",
36
36
  "better-auth": "^1.7.12"
37
37
  }