@ovok/core 0.4.7 → 0.4.9

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 CHANGED
@@ -362,6 +362,60 @@ const { data: translations } = useLocalizations("en-US");
362
362
  Hooks are additive to the existing `OvokProvider`, observation hooks, auth methods, and offline
363
363
  queue. Billing remains outside the SDK contract.
364
364
 
365
+ ## CMS content
366
+
367
+ Published Ovok CMS documents (legal pages, release notes, or any other collection) are read with
368
+ `getCmsDocument(collection, slug)` and `listCmsDocuments(collection)`, or the `useCmsDocument`,
369
+ `useCmsDocuments`, `useLegalPage`, and `useReleaseNotes` hooks. They are separate from the older
370
+ `searchContent` and `useContent` reads.
371
+
372
+ ```tsx
373
+ const { data: terms, error } = useLegalPage("terms-and-conditions", { tenantCode });
374
+ const { data: notes } = useReleaseNotes({ limit: 10 });
375
+ const faq = await client.listCmsDocuments("faq", { page: 2 });
376
+ ```
377
+
378
+ The backend allows three kinds of read:
379
+
380
+ - **Signed in**: the session reads its own tenant's content with no API key. Naming another
381
+ tenant without a key is refused with 403.
382
+ - **Legal pages and translations**: no key and no session are needed. Before sign-in, pass the
383
+ tenant's `tenantCode`.
384
+ - **Anything else**: it needs the public API key, and is a 401 without one. This SDK does not
385
+ send a key.
386
+
387
+ Signed out, a read rejects with `SignInRequiredError` (`isSignInRequiredError(error)`) before
388
+ any request is sent, unless it passes `allowSignedOut: true` and a `tenantCode`. `getLegalPage`,
389
+ `getTranslations` and their hooks turn `allowSignedOut` on by default; pass it yourself only for
390
+ another collection the backend serves without a key or session. This matters because
391
+ `MedplumClient` answers any 401 by clearing its storage and calling `onUnauthenticated`. A hook
392
+ that rendered before sign-in needs `reload()` once the session exists.
393
+
394
+ `locale` takes an app language or a CMS locale (`en`, `de`, `fr`, `es`). `toCmsLocale` maps
395
+ `de-CH` to `de`, and a language the CMS does not carry to `en`. The hooks default to the
396
+ runtime's language (`defaultCmsLocale()`), and `""` counts as unset. A change of language that
397
+ maps to the same CMS locale does not read again. Lists use the CMS envelope (`OvokCmsPage`):
398
+ `prevPage` and `nextPage` are `null` at either end, and `limit` is at most 50. Release notes are
399
+ newest first unless `sort` says otherwise. The backend merges in a shared tenant's documents.
400
+
401
+ ### Translations
402
+
403
+ `getTranslations()` and `useTranslations()` read the whole `translations` collection as one tree
404
+ (`OvokTranslations`): each document's `slug` is a group, and each of its `strings` rows puts its
405
+ `value` at its `key`, nested at the dots.
406
+
407
+ ```tsx
408
+ const { data: translations } = useTranslations({ tenantCode });
409
+ // { "sign-in": { title: "Anmelden", errors: { required: "Pflichtfeld" } } }
410
+ ```
411
+
412
+ The CMS answers a row with no text in the locale in English. A row with no text at all, and a row
413
+ whose key is taken (or clashes with a nested key), are left out. Nothing is kept on the device, so
414
+ offline the app shows its own and the SDK's built-in text.
415
+
416
+ These routes ship with an Ovok Core backend release. Deploy that backend before releasing this
417
+ SDK version.
418
+
365
419
  ## Offline measurements
366
420
 
367
421
  Pass a durable Medplum storage adapter and opt in to queue measurements while offline:
@@ -0,0 +1,3 @@
1
+ export * from "./locale";
2
+ export * from "./methods";
3
+ export * from "./types";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./locale"), exports);
18
+ __exportStar(require("./methods"), exports);
19
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,11 @@
1
+ /** The Ovok CMS locales (the CMS's `CMS_LOCALES`); `en` is its default and fallback. */
2
+ export declare const OVOK_CMS_LOCALES: readonly ["en", "de", "fr", "es"];
3
+ export type OvokCmsLocale = (typeof OVOK_CMS_LOCALES)[number];
4
+ export declare const OVOK_CMS_DEFAULT_LOCALE: OvokCmsLocale;
5
+ /**
6
+ * The CMS locale for an app language: "de-DE" and "de_CH" read "de"; a
7
+ * language the CMS doesn't carry reads its default, "en".
8
+ */
9
+ export declare function toCmsLocale(language?: string | null): OvokCmsLocale;
10
+ /** The runtime's language (browser, device or server), as a CMS locale. */
11
+ export declare function defaultCmsLocale(): OvokCmsLocale;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OVOK_CMS_DEFAULT_LOCALE = exports.OVOK_CMS_LOCALES = void 0;
4
+ exports.toCmsLocale = toCmsLocale;
5
+ exports.defaultCmsLocale = defaultCmsLocale;
6
+ /** The Ovok CMS locales (the CMS's `CMS_LOCALES`); `en` is its default and fallback. */
7
+ exports.OVOK_CMS_LOCALES = ["en", "de", "fr", "es"];
8
+ exports.OVOK_CMS_DEFAULT_LOCALE = "en";
9
+ /**
10
+ * The CMS locale for an app language: "de-DE" and "de_CH" read "de"; a
11
+ * language the CMS doesn't carry reads its default, "en".
12
+ */
13
+ function toCmsLocale(language) {
14
+ const primary = (language !== null && language !== void 0 ? language : "").split(/[-_]/)[0].toLowerCase();
15
+ return exports.OVOK_CMS_LOCALES.includes(primary)
16
+ ? primary
17
+ : exports.OVOK_CMS_DEFAULT_LOCALE;
18
+ }
19
+ /** The runtime's language (browser, device or server), as a CMS locale. */
20
+ function defaultCmsLocale() {
21
+ try {
22
+ return toCmsLocale(Intl.DateTimeFormat().resolvedOptions().locale);
23
+ }
24
+ catch (_a) {
25
+ return exports.OVOK_CMS_DEFAULT_LOCALE;
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ import { OvokClient } from "../ovok-client";
2
+ import { OvokCmsDocument, OvokCmsListOptions, OvokCmsOptions, OvokCmsPage, OvokTranslations } from "./types";
3
+ /**
4
+ * A published document of a CMS collection by slug, the shared tenant's when
5
+ * the tenant has none of its own. Signed out, it is read only with
6
+ * `allowSignedOut` and a `tenantCode`; otherwise it rejects with
7
+ * `SignInRequiredError`.
8
+ */
9
+ export declare function getCmsDocument<T extends object = OvokCmsDocument>(this: OvokClient, collection: string, slug: string, options?: OvokCmsOptions): Promise<T>;
10
+ /**
11
+ * A page of a CMS collection's published documents, merged with the shared
12
+ * tenant's. Signed out, as `getCmsDocument`.
13
+ */
14
+ export declare function listCmsDocuments<T extends object = OvokCmsDocument>(this: OvokClient, collection: string, options?: OvokCmsListOptions): Promise<OvokCmsPage<T>>;
15
+ /** A legal page ("terms-and-conditions", "data-privacy", …). Readable before sign-in with a tenant code. */
16
+ export declare function getLegalPage(this: OvokClient, slug: string, options?: OvokCmsOptions): Promise<OvokCmsDocument>;
17
+ /** Release notes, newest first unless `sort` says otherwise. Needs a session. */
18
+ export declare function listReleaseNotes(this: OvokClient, options?: OvokCmsListOptions): Promise<OvokCmsPage>;
19
+ /**
20
+ * The `translations` collection as one tree: a group per document slug, and
21
+ * each of its `strings` rows' `value` at its `key`, nested at the dots. Rows
22
+ * without a text, and rows whose path is taken, are left out. Readable before
23
+ * sign-in with a tenant code. Nothing is kept on the device: on native the
24
+ * client's storage is SecureStore, read whole at every launch.
25
+ */
26
+ export declare function getTranslations(this: OvokClient, options?: OvokCmsOptions): Promise<OvokTranslations>;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCmsDocument = getCmsDocument;
4
+ exports.listCmsDocuments = listCmsDocuments;
5
+ exports.getLegalPage = getLegalPage;
6
+ exports.listReleaseNotes = listReleaseNotes;
7
+ exports.getTranslations = getTranslations;
8
+ const sign_in_required_error_1 = require("../errors/sign-in-required-error");
9
+ const locale_1 = require("./locale");
10
+ const collectionPath = (collection) => `/v1/public/cms/collections/${encodeURIComponent(collection)}/items`;
11
+ const cmsQuery = ({ locale, tenantCode, environment, page, limit, sort }) => {
12
+ const params = new URLSearchParams();
13
+ if (locale) {
14
+ params.set("locale", (0, locale_1.toCmsLocale)(locale));
15
+ }
16
+ if (tenantCode) {
17
+ params.set("tenant", tenantCode);
18
+ }
19
+ if (environment) {
20
+ params.set("environment", environment);
21
+ }
22
+ if (page !== undefined) {
23
+ params.set("page", String(page));
24
+ }
25
+ if (limit !== undefined) {
26
+ params.set("limit", String(limit));
27
+ }
28
+ if (sort) {
29
+ params.set("sort", sort);
30
+ }
31
+ const query = params.toString();
32
+ return query ? `?${query}` : "";
33
+ };
34
+ /**
35
+ * Signed out, the backend serves only a few collections, read by tenant code;
36
+ * any other read is a 401, which MedplumClient answers by clearing its
37
+ * storage. So a signed-out read is refused here, before a request, unless it
38
+ * opts in with `allowSignedOut` and names its tenant.
39
+ */
40
+ const assertReadable = (client, collection, { allowSignedOut, tenantCode }) => {
41
+ if (!client.getActiveLogin() && !(allowSignedOut && tenantCode)) {
42
+ throw new sign_in_required_error_1.SignInRequiredError(`Sign in to read the CMS collection "${collection}".`);
43
+ }
44
+ };
45
+ /**
46
+ * A published document of a CMS collection by slug, the shared tenant's when
47
+ * the tenant has none of its own. Signed out, it is read only with
48
+ * `allowSignedOut` and a `tenantCode`; otherwise it rejects with
49
+ * `SignInRequiredError`.
50
+ */
51
+ async function getCmsDocument(collection, slug, options = {}) {
52
+ if (!slug) {
53
+ throw new Error("A CMS document read needs a slug.");
54
+ }
55
+ assertReadable(this, collection, options);
56
+ return this.get(`${collectionPath(collection)}/${encodeURIComponent(slug)}${cmsQuery(options)}`);
57
+ }
58
+ /**
59
+ * A page of a CMS collection's published documents, merged with the shared
60
+ * tenant's. Signed out, as `getCmsDocument`.
61
+ */
62
+ async function listCmsDocuments(collection, options = {}) {
63
+ assertReadable(this, collection, options);
64
+ return this.get(`${collectionPath(collection)}${cmsQuery(options)}`);
65
+ }
66
+ /** A legal page ("terms-and-conditions", "data-privacy", …). Readable before sign-in with a tenant code. */
67
+ function getLegalPage(slug, options = {}) {
68
+ var _a;
69
+ return this.getCmsDocument("legal-pages", slug, { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true });
70
+ }
71
+ /** Release notes, newest first unless `sort` says otherwise. Needs a session. */
72
+ function listReleaseNotes(options = {}) {
73
+ var _a;
74
+ return this.listCmsDocuments("release-notes", { ...options, sort: (_a = options.sort) !== null && _a !== void 0 ? _a : "-publishedAt" });
75
+ }
76
+ /** A translations read takes pages of 50 documents, the CMS's most, and at most this many. */
77
+ const MAX_TRANSLATION_PAGES = 20;
78
+ /** Keys that would reach a prototype instead of the tree. */
79
+ const UNSAFE_KEYS = ["__proto__", "constructor", "prototype"];
80
+ const isText = (value) => typeof value === "string" && value !== "";
81
+ const hasOwn = (tree, key) => Object.prototype.hasOwnProperty.call(tree, key);
82
+ /**
83
+ * Sets `text` at `path` unless something is in the way: a text where a group
84
+ * is needed, or anything where the text goes, so the first row wins. Keys are
85
+ * CMS input, so a path through a prototype key is skipped too.
86
+ */
87
+ const setTranslation = (tree, path, text) => {
88
+ if (path.some((key) => UNSAFE_KEYS.includes(key))) {
89
+ return;
90
+ }
91
+ let node = tree;
92
+ for (const key of path.slice(0, -1)) {
93
+ const next = hasOwn(node, key) ? node[key] : (node[key] = {});
94
+ if (typeof next === "string") {
95
+ return;
96
+ }
97
+ node = next;
98
+ }
99
+ const leaf = path[path.length - 1];
100
+ if (!hasOwn(node, leaf)) {
101
+ node[leaf] = text;
102
+ }
103
+ };
104
+ /** Adds each document's `strings` rows under its slug; a row needs a key and a text. */
105
+ const addTranslations = (translations, docs) => {
106
+ for (const { slug, strings } of docs) {
107
+ if (!isText(slug) || !Array.isArray(strings)) {
108
+ continue;
109
+ }
110
+ for (const row of strings) {
111
+ const { key, value } = (row !== null && row !== void 0 ? row : {});
112
+ if (isText(key) && isText(value)) {
113
+ setTranslation(translations, [slug, ...key.split(".")], value);
114
+ }
115
+ }
116
+ }
117
+ };
118
+ /**
119
+ * The `translations` collection as one tree: a group per document slug, and
120
+ * each of its `strings` rows' `value` at its `key`, nested at the dots. Rows
121
+ * without a text, and rows whose path is taken, are left out. Readable before
122
+ * sign-in with a tenant code. Nothing is kept on the device: on native the
123
+ * client's storage is SecureStore, read whole at every launch.
124
+ */
125
+ async function getTranslations(options = {}) {
126
+ var _a;
127
+ const readOptions = { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true };
128
+ const translations = {};
129
+ for (let page = 1; page <= MAX_TRANSLATION_PAGES; page++) {
130
+ const { docs, hasNextPage } = await this.listCmsDocuments("translations", { ...readOptions, page, limit: 50 });
131
+ addTranslations(translations, docs);
132
+ if (!hasNextPage) {
133
+ break;
134
+ }
135
+ }
136
+ return translations;
137
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * A published document of an Ovok CMS collection (`legal-pages`,
3
+ * `release-notes`, …). Fields beyond these depend on the collection.
4
+ */
5
+ export type OvokCmsDocument = {
6
+ id: number | string;
7
+ slug?: string | null;
8
+ title?: string | null;
9
+ excerpt?: string | null;
10
+ /** Lexical rich text (JSON), as the CMS stores it. */
11
+ body?: unknown;
12
+ tags?: string[] | null;
13
+ status: string;
14
+ publishedAt?: string | null;
15
+ effectiveAt?: string | null;
16
+ updatedAt?: string | null;
17
+ [field: string]: unknown;
18
+ };
19
+ /** A page of published documents (the CMS list envelope). */
20
+ export type OvokCmsPage<T = OvokCmsDocument> = {
21
+ docs: T[];
22
+ totalDocs: number;
23
+ limit: number;
24
+ totalPages: number;
25
+ page: number;
26
+ /** The 1-based position of this page's first document in the whole list. */
27
+ pagingCounter: number;
28
+ hasPrevPage: boolean;
29
+ hasNextPage: boolean;
30
+ /** `null` on the first page. */
31
+ prevPage: number | null;
32
+ /** `null` on the last page. */
33
+ nextPage: number | null;
34
+ };
35
+ export type OvokCmsOptions = {
36
+ /**
37
+ * An app language ("de-DE") or a CMS locale ("de"), mapped with
38
+ * `toCmsLocale`. Omitted or "", the CMS answers in its default locale; the
39
+ * hooks send the runtime's language instead.
40
+ */
41
+ locale?: string;
42
+ /**
43
+ * The tenant's code. Signed out, a read needs this and `allowSignedOut`.
44
+ * Signed in, the session's own tenant is read; naming another tenant is
45
+ * refused (403).
46
+ */
47
+ tenantCode?: string;
48
+ /** Omitted, the backend's own. */
49
+ environment?: "dev" | "staging" | "prod";
50
+ /**
51
+ * Read signed out. Only for collections the backend serves without a key or
52
+ * session: legal pages and translations, which turn it on by default. It
53
+ * needs `tenantCode`; without both, a signed-out read is refused before a
54
+ * request, because a 401 makes MedplumClient clear its storage.
55
+ */
56
+ allowSignedOut?: boolean;
57
+ };
58
+ /** Translation keys, nested at their dots, down to the text in one locale. */
59
+ export type OvokTranslationTree = {
60
+ [key: string]: string | OvokTranslationTree;
61
+ };
62
+ /** The `translations` collection: a tree per group, named by the document's slug ("sign-in"). */
63
+ export type OvokTranslations = {
64
+ [group: string]: OvokTranslationTree;
65
+ };
66
+ export type OvokCmsListOptions = OvokCmsOptions & {
67
+ page?: number;
68
+ /** At most 50. */
69
+ limit?: number;
70
+ /** A top-level field, "-" for descending, e.g. "-publishedAt". */
71
+ sort?: string;
72
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A typed error for a read that needs a signed-in session when there is none.
3
+ * It is thrown before any request: MedplumClient answers a 401 by clearing
4
+ * its storage and calling `onUnauthenticated`.
5
+ */
6
+ export declare class SignInRequiredError extends Error {
7
+ readonly code: "sign_in_required";
8
+ constructor(message: string);
9
+ }
10
+ export declare function isSignInRequiredError(error: unknown): error is SignInRequiredError;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SignInRequiredError = void 0;
4
+ exports.isSignInRequiredError = isSignInRequiredError;
5
+ /**
6
+ * A typed error for a read that needs a signed-in session when there is none.
7
+ * It is thrown before any request: MedplumClient answers a 401 by clearing
8
+ * its storage and calling `onUnauthenticated`.
9
+ */
10
+ class SignInRequiredError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.code = "sign_in_required";
14
+ this.name = "SignInRequiredError";
15
+ }
16
+ }
17
+ exports.SignInRequiredError = SignInRequiredError;
18
+ function isSignInRequiredError(error) {
19
+ return error instanceof SignInRequiredError;
20
+ }
@@ -5,6 +5,7 @@ import * as aiFhirMethods from "./ai-fhir/methods";
5
5
  import * as authMethods from "./auth/methods";
6
6
  import * as botMethods from "./bot/methods";
7
7
  import * as aiMethods from "./chat/ai/methods";
8
+ import * as cmsMethods from "./cms/methods";
8
9
  import * as emailMethods from "./email";
9
10
  import * as experienceMethods from "./experience/methods";
10
11
  import * as inviteMethods from "./invites/methods";
@@ -62,7 +63,7 @@ export declare class OvokClient extends MedplumClient {
62
63
  */
63
64
  getSubscriptionManager(): SubscriptionManager;
64
65
  }
65
- type Methods = typeof authMethods & typeof observationMethods & typeof questionnaireResponseMethods & typeof aiMethods & typeof aiFhirMethods & typeof botMethods & typeof slimMethods & typeof emailMethods & typeof platformMethods & typeof experienceMethods & typeof inviteMethods & typeof patientMethods & typeof patientSharingMethods & typeof signalsMethods;
66
+ type Methods = typeof authMethods & typeof observationMethods & typeof questionnaireResponseMethods & typeof aiMethods & typeof aiFhirMethods & typeof botMethods & typeof slimMethods & typeof emailMethods & typeof cmsMethods & typeof platformMethods & typeof experienceMethods & typeof inviteMethods & typeof patientMethods & typeof patientSharingMethods & typeof signalsMethods;
66
67
  type Omitted<T> = Omit<T, "executeBot">;
67
68
  declare module "./ovok-client" {
68
69
  interface OvokClient extends Omitted<Methods> {
@@ -41,6 +41,7 @@ const aiFhirMethods = __importStar(require("./ai-fhir/methods"));
41
41
  const authMethods = __importStar(require("./auth/methods"));
42
42
  const botMethods = __importStar(require("./bot/methods"));
43
43
  const aiMethods = __importStar(require("./chat/ai/methods"));
44
+ const cmsMethods = __importStar(require("./cms/methods"));
44
45
  const emailMethods = __importStar(require("./email"));
45
46
  const rate_limit_error_1 = require("./errors/rate-limit-error");
46
47
  const experienceMethods = __importStar(require("./experience/methods"));
@@ -76,6 +77,7 @@ class OvokClient extends core_1.MedplumClient {
76
77
  this.bindMethods(botMethods);
77
78
  this.bindMethods(slimMethods);
78
79
  this.bindMethods(emailMethods);
80
+ this.bindMethods(cmsMethods);
79
81
  this.bindMethods(platformMethods);
80
82
  this.bindMethods(experienceMethods);
81
83
  this.bindMethods(inviteMethods);
@@ -57,6 +57,8 @@ exports.OVOK_CORE_API_REQUIREMENTS = {
57
57
  { method: "GET", path: "/cms/:type/:language/:key", group: "Content" },
58
58
  { method: "DELETE", path: "/cms/:type/:language/:key", group: "Content" },
59
59
  { method: "GET", path: "/cms/:type/:language", group: "Content" },
60
+ { method: "GET", path: "/v1/public/cms/collections/:collection/items", group: "Cms" },
61
+ { method: "GET", path: "/v1/public/cms/collections/:collection/items/:slug", group: "Cms" },
60
62
  { method: "GET", path: "/video-call/livekit/access-permission/:appointmentId", group: "Video" },
61
63
  { method: "POST", path: "/video-call/livekit/guest-access/:appointmentId", group: "Video" },
62
64
  { method: "POST", path: "/video-call/livekit/user-access/:appointmentId", group: "Video" },
@@ -48,7 +48,7 @@ exports.OVOK_CORE_REQUIREMENTS = {
48
48
  description: "The FHIR surface a server must provide for the @ovok/core client library's own methods to function.",
49
49
  software: {
50
50
  name: "@ovok/core",
51
- version: "0.4.7",
51
+ version: "0.4.9",
52
52
  },
53
53
  fhirVersion: "4.0.1",
54
54
  format: ["json"],
@@ -0,0 +1,18 @@
1
+ import { OvokCmsDocument, OvokCmsListOptions, OvokCmsOptions, OvokCmsPage, OvokTranslations } from "../client/cms";
2
+ import { OvokRequestState } from "./api-hooks";
3
+ /**
4
+ * A published CMS document by slug, in `options.locale` or else the runtime's
5
+ * language. Waits while `slug` is empty; a new CMS locale reads it again.
6
+ */
7
+ export declare const useCmsDocument: <T extends object = OvokCmsDocument>(collection: string, slug: string | undefined, options?: OvokCmsOptions) => OvokRequestState<T>;
8
+ /** A page of a CMS collection, in `options.locale` or else the runtime's language. */
9
+ export declare const useCmsDocuments: <T extends object = OvokCmsDocument>(collection: string, options?: OvokCmsListOptions) => OvokRequestState<OvokCmsPage<T>>;
10
+ /** A legal page by slug; with `tenantCode`, readable before sign-in. */
11
+ export declare const useLegalPage: (slug: string | undefined, options?: OvokCmsOptions) => OvokRequestState<OvokCmsDocument>;
12
+ /** Release notes, newest first unless `sort` says otherwise. Needs a session. */
13
+ export declare const useReleaseNotes: (options?: OvokCmsListOptions) => OvokRequestState<OvokCmsPage>;
14
+ /**
15
+ * The `translations` collection as one tree, in `options.locale` or else the
16
+ * runtime's language; with `tenantCode`, readable before sign-in.
17
+ */
18
+ export declare const useTranslations: (options?: OvokCmsOptions) => OvokRequestState<OvokTranslations>;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ "use client";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.useTranslations = exports.useReleaseNotes = exports.useLegalPage = exports.useCmsDocuments = exports.useCmsDocument = void 0;
38
+ const React = __importStar(require("react"));
39
+ const cms_1 = require("../client/cms");
40
+ const api_hooks_1 = require("./api-hooks");
41
+ const index_1 = require("./index");
42
+ /**
43
+ * A published CMS document by slug, in `options.locale` or else the runtime's
44
+ * language. Waits while `slug` is empty; a new CMS locale reads it again.
45
+ */
46
+ const useCmsDocument = (collection, slug, options = {}) => {
47
+ const client = (0, index_1.useClient)();
48
+ const { tenantCode, environment, allowSignedOut } = options;
49
+ const locale = React.useMemo(() => (0, cms_1.toCmsLocale)(options.locale || (0, cms_1.defaultCmsLocale)()), [options.locale]);
50
+ const load = React.useCallback(() => client.getCmsDocument(collection, slug, { locale, tenantCode, environment, allowSignedOut }), [client, collection, slug, locale, tenantCode, environment, allowSignedOut]);
51
+ return (0, api_hooks_1.useOvokRequest)(load, Boolean(slug));
52
+ };
53
+ exports.useCmsDocument = useCmsDocument;
54
+ /** A page of a CMS collection, in `options.locale` or else the runtime's language. */
55
+ const useCmsDocuments = (collection, options = {}) => {
56
+ const client = (0, index_1.useClient)();
57
+ const { tenantCode, environment, allowSignedOut, page, limit, sort } = options;
58
+ const locale = React.useMemo(() => (0, cms_1.toCmsLocale)(options.locale || (0, cms_1.defaultCmsLocale)()), [options.locale]);
59
+ const load = React.useCallback(() => client.listCmsDocuments(collection, { locale, tenantCode, environment, allowSignedOut, page, limit, sort }), [client, collection, locale, tenantCode, environment, allowSignedOut, page, limit, sort]);
60
+ return (0, api_hooks_1.useOvokRequest)(load);
61
+ };
62
+ exports.useCmsDocuments = useCmsDocuments;
63
+ /** A legal page by slug; with `tenantCode`, readable before sign-in. */
64
+ const useLegalPage = (slug, options = {}) => { var _a; return (0, exports.useCmsDocument)("legal-pages", slug, { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true }); };
65
+ exports.useLegalPage = useLegalPage;
66
+ /** Release notes, newest first unless `sort` says otherwise. Needs a session. */
67
+ const useReleaseNotes = (options = {}) => { var _a; return (0, exports.useCmsDocuments)("release-notes", { ...options, sort: (_a = options.sort) !== null && _a !== void 0 ? _a : "-publishedAt" }); };
68
+ exports.useReleaseNotes = useReleaseNotes;
69
+ /**
70
+ * The `translations` collection as one tree, in `options.locale` or else the
71
+ * runtime's language; with `tenantCode`, readable before sign-in.
72
+ */
73
+ const useTranslations = (options = {}) => {
74
+ const client = (0, index_1.useClient)();
75
+ const { tenantCode, environment, allowSignedOut } = options;
76
+ const locale = React.useMemo(() => (0, cms_1.toCmsLocale)(options.locale || (0, cms_1.defaultCmsLocale)()), [options.locale]);
77
+ const load = React.useCallback(() => client.getTranslations({ locale, tenantCode, environment, allowSignedOut }), [client, locale, tenantCode, environment, allowSignedOut]);
78
+ return (0, api_hooks_1.useOvokRequest)(load);
79
+ };
80
+ exports.useTranslations = useTranslations;
@@ -7,9 +7,11 @@ export declare const OvokProvider: (props: React.PropsWithChildren<{
7
7
  }>) => React.JSX.Element;
8
8
  export { observationsToMeasurements, useEcgRecording, useObservations, useSaveMeasurement, useUrineTests, } from "./observation-hooks";
9
9
  export { useAdministeredProjects, useSlimDeviceTelemetry, useSlimDevices, useSlimNotifications, useSlimPatients, useCapabilityStatement, useOvokMutation, useOvokRequest, useProjectFeatures, useProjectSettings, usePublicApiEndpoints, useRecentSlimNotifications, } from "./api-hooks";
10
+ export type { OvokMutationState, OvokRequestState } from "./api-hooks";
10
11
  export { useConsentSignature, useContent, useCreateVideoCallAppointment, useCreateVideoCallUserAccess, useDocuments, useVideoCallAccess, useVideoCallAppointments, } from "./experience-hooks";
11
12
  export { useI18nextDocument, useLocales, useLocalizations, useUpdateI18nextDocument, useUpdateLocalization, useUpdateProjectTextSetting, } from "./platform-hooks";
12
13
  export { useEmailTemplates } from "./email-hooks";
14
+ export { useCmsDocument, useCmsDocuments, useLegalPage, useReleaseNotes, useTranslations, } from "./cms-hooks";
13
15
  export { useAcceptInvite, useInvitePatient, useInvitePractitioner, } from "./invites-hooks";
14
16
  export { useCurrentPatientProfile, useExtractQuestionnaireObservations, usePatientAppointments, usePatientCarePlans, usePatientDevices, usePatientDeviceTelemetry, usePatientDeviceTelemetryHistory, usePatientMeasurement, usePatientMeasurementHistory, usePatientDashboard, usePatientLatestObservations, usePatientNotifications, usePatientQuestionnaireResponses, usePatientQuestionnaires, usePatientSchedules, usePatientSlots, usePopulateQuestionnaire, useSubmitQuestionnaireResponse, } from "./patient-hooks";
15
17
  export { useCreatePatientSharingInvite, usePatientSharing, usePatientSharingSettings, useRedeemPatientSharingInvite, useRevokePatientSharing, useUpdatePatientSharingSettings, } from "./patient-sharing-hooks";
@@ -34,8 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  };
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.usePatientAppointments = exports.useExtractQuestionnaireObservations = exports.useCurrentPatientProfile = exports.useInvitePractitioner = exports.useInvitePatient = exports.useAcceptInvite = exports.useEmailTemplates = exports.useUpdateProjectTextSetting = exports.useUpdateLocalization = exports.useUpdateI18nextDocument = exports.useLocalizations = exports.useLocales = exports.useI18nextDocument = exports.useVideoCallAppointments = exports.useVideoCallAccess = exports.useDocuments = exports.useCreateVideoCallUserAccess = exports.useCreateVideoCallAppointment = exports.useContent = exports.useConsentSignature = exports.useRecentSlimNotifications = exports.usePublicApiEndpoints = exports.useProjectSettings = exports.useProjectFeatures = exports.useOvokRequest = exports.useOvokMutation = exports.useCapabilityStatement = exports.useSlimPatients = exports.useSlimNotifications = exports.useSlimDevices = exports.useSlimDeviceTelemetry = exports.useAdministeredProjects = exports.useUrineTests = exports.useSaveMeasurement = exports.useObservations = exports.useEcgRecording = exports.observationsToMeasurements = exports.OvokProvider = exports.useClient = exports.useCachedBinaryUrl = exports.reactContext = exports.useSubscription = exports.useSearchResources = exports.useSearchOne = exports.useSearch = exports.useResource = exports.usePrevious = exports.useMedplumProfile = exports.useMedplumNavigate = exports.useMedplumContext = void 0;
38
- exports.useUpdateSignalsSettings = exports.useSignalsThresholds = exports.useSignalsSettings = exports.useSignalsAlerts = exports.useSignalsAlert = exports.useSetSignalsThreshold = exports.useRemoveSignalsThreshold = exports.useEnableSignalsForPatient = exports.useAcknowledgeSignalsAlert = exports.useUpdatePatientSharingSettings = exports.useRevokePatientSharing = exports.useRedeemPatientSharingInvite = exports.usePatientSharingSettings = exports.usePatientSharing = exports.useCreatePatientSharingInvite = exports.useSubmitQuestionnaireResponse = exports.usePopulateQuestionnaire = exports.usePatientSlots = exports.usePatientSchedules = exports.usePatientQuestionnaires = exports.usePatientQuestionnaireResponses = exports.usePatientNotifications = exports.usePatientLatestObservations = exports.usePatientDashboard = exports.usePatientMeasurementHistory = exports.usePatientMeasurement = exports.usePatientDeviceTelemetryHistory = exports.usePatientDeviceTelemetry = exports.usePatientDevices = exports.usePatientCarePlans = void 0;
37
+ exports.useAcceptInvite = exports.useTranslations = exports.useReleaseNotes = exports.useLegalPage = exports.useCmsDocuments = exports.useCmsDocument = exports.useEmailTemplates = exports.useUpdateProjectTextSetting = exports.useUpdateLocalization = exports.useUpdateI18nextDocument = exports.useLocalizations = exports.useLocales = exports.useI18nextDocument = exports.useVideoCallAppointments = exports.useVideoCallAccess = exports.useDocuments = exports.useCreateVideoCallUserAccess = exports.useCreateVideoCallAppointment = exports.useContent = exports.useConsentSignature = exports.useRecentSlimNotifications = exports.usePublicApiEndpoints = exports.useProjectSettings = exports.useProjectFeatures = exports.useOvokRequest = exports.useOvokMutation = exports.useCapabilityStatement = exports.useSlimPatients = exports.useSlimNotifications = exports.useSlimDevices = exports.useSlimDeviceTelemetry = exports.useAdministeredProjects = exports.useUrineTests = exports.useSaveMeasurement = exports.useObservations = exports.useEcgRecording = exports.observationsToMeasurements = exports.OvokProvider = exports.useClient = exports.useCachedBinaryUrl = exports.reactContext = exports.useSubscription = exports.useSearchResources = exports.useSearchOne = exports.useSearch = exports.useResource = exports.usePrevious = exports.useMedplumProfile = exports.useMedplumNavigate = exports.useMedplumContext = void 0;
38
+ exports.useUpdateSignalsSettings = exports.useSignalsThresholds = exports.useSignalsSettings = exports.useSignalsAlerts = exports.useSignalsAlert = exports.useSetSignalsThreshold = exports.useRemoveSignalsThreshold = exports.useEnableSignalsForPatient = exports.useAcknowledgeSignalsAlert = exports.useUpdatePatientSharingSettings = exports.useRevokePatientSharing = exports.useRedeemPatientSharingInvite = exports.usePatientSharingSettings = exports.usePatientSharing = exports.useCreatePatientSharingInvite = exports.useSubmitQuestionnaireResponse = exports.usePopulateQuestionnaire = exports.usePatientSlots = exports.usePatientSchedules = exports.usePatientQuestionnaires = exports.usePatientQuestionnaireResponses = exports.usePatientNotifications = exports.usePatientLatestObservations = exports.usePatientDashboard = exports.usePatientMeasurementHistory = exports.usePatientMeasurement = exports.usePatientDeviceTelemetryHistory = exports.usePatientDeviceTelemetry = exports.usePatientDevices = exports.usePatientCarePlans = exports.usePatientAppointments = exports.useExtractQuestionnaireObservations = exports.useCurrentPatientProfile = exports.useInvitePractitioner = exports.useInvitePatient = void 0;
39
39
  /*
40
40
  * The only React-dependent module in the package.
41
41
  *
@@ -107,6 +107,12 @@ Object.defineProperty(exports, "useUpdateLocalization", { enumerable: true, get:
107
107
  Object.defineProperty(exports, "useUpdateProjectTextSetting", { enumerable: true, get: function () { return platform_hooks_1.useUpdateProjectTextSetting; } });
108
108
  var email_hooks_1 = require("./email-hooks");
109
109
  Object.defineProperty(exports, "useEmailTemplates", { enumerable: true, get: function () { return email_hooks_1.useEmailTemplates; } });
110
+ var cms_hooks_1 = require("./cms-hooks");
111
+ Object.defineProperty(exports, "useCmsDocument", { enumerable: true, get: function () { return cms_hooks_1.useCmsDocument; } });
112
+ Object.defineProperty(exports, "useCmsDocuments", { enumerable: true, get: function () { return cms_hooks_1.useCmsDocuments; } });
113
+ Object.defineProperty(exports, "useLegalPage", { enumerable: true, get: function () { return cms_hooks_1.useLegalPage; } });
114
+ Object.defineProperty(exports, "useReleaseNotes", { enumerable: true, get: function () { return cms_hooks_1.useReleaseNotes; } });
115
+ Object.defineProperty(exports, "useTranslations", { enumerable: true, get: function () { return cms_hooks_1.useTranslations; } });
110
116
  var invites_hooks_1 = require("./invites-hooks");
111
117
  Object.defineProperty(exports, "useAcceptInvite", { enumerable: true, get: function () { return invites_hooks_1.useAcceptInvite; } });
112
118
  Object.defineProperty(exports, "useInvitePatient", { enumerable: true, get: function () { return invites_hooks_1.useInvitePatient; } });
package/dist/index.d.ts CHANGED
@@ -924,6 +924,7 @@ declare const Medplum: {
924
924
  export { Medplum };
925
925
  export { OvokClient } from "./client/ovok-client";
926
926
  export * from "./client/errors/rate-limit-error";
927
+ export * from "./client/errors/sign-in-required-error";
927
928
  export * from "./client/errors/ovok-error";
928
929
  export * from "./client/auth/errors";
929
930
  export type { RegisterBody } from "./client/auth/types/RegisterBody";
@@ -941,6 +942,7 @@ export * from "./conformance/api-requirements";
941
942
  export * from "./conformance/capability";
942
943
  export * from "./client/platform/types";
943
944
  export * from "./client/email";
945
+ export * from "./client/cms";
944
946
  export * from "./client/invites";
945
947
  export * from "./client/slim/types";
946
948
  export * from "./client/experience/types";
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ exports.Medplum = Medplum;
45
45
  var ovok_client_1 = require("./client/ovok-client");
46
46
  Object.defineProperty(exports, "OvokClient", { enumerable: true, get: function () { return ovok_client_1.OvokClient; } });
47
47
  __exportStar(require("./client/errors/rate-limit-error"), exports);
48
+ __exportStar(require("./client/errors/sign-in-required-error"), exports);
48
49
  __exportStar(require("./client/errors/ovok-error"), exports);
49
50
  __exportStar(require("./client/auth/errors"), exports);
50
51
  var ovok_client_2 = require("./client/ovok-client");
@@ -62,6 +63,7 @@ __exportStar(require("./conformance/api-requirements"), exports);
62
63
  __exportStar(require("./conformance/capability"), exports);
63
64
  __exportStar(require("./client/platform/types"), exports);
64
65
  __exportStar(require("./client/email"), exports);
66
+ __exportStar(require("./client/cms"), exports);
65
67
  __exportStar(require("./client/invites"), exports);
66
68
  __exportStar(require("./client/slim/types"), exports);
67
69
  __exportStar(require("./client/experience/types"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovok/core",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",