@bardioc/app-sdk 1.0.0 → 1.0.2

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
@@ -113,7 +113,7 @@ response body as `details`.
113
113
  ```ts
114
114
  import { installDevHost, isDevStandalone } from '@bardioc/app-sdk/dev';
115
115
 
116
- if (isDevStandalone()) installDevHost({ appId: 'my-app' });
116
+ if (isDevStandalone()) installDevHost();
117
117
  ```
118
118
 
119
119
  The dev host answers the same contract as the Bardioc host: BFF calls go through the dev-auth
package/dist/client.d.ts CHANGED
@@ -96,6 +96,10 @@ declare function open(port: MessagePort, initial: Context): {
96
96
  launch: (id: string) => Promise<{
97
97
  windowId: string | null;
98
98
  }>;
99
+ /** The caller's settings of the developer application `id`, as the host holds them. */
100
+ getConfig: (id: string) => Promise<import("./contract.js").DevAppConfig>;
101
+ /** Replaces them with the edited document; the host applies the answer at once. */
102
+ saveConfig: (id: string, data: Record<string, unknown>) => Promise<import("./contract.js").DevAppConfig>;
99
103
  };
100
104
  };
101
105
  export type Bardioc = ReturnType<typeof open>;
package/dist/client.js CHANGED
@@ -92,6 +92,10 @@ function open(port, initial) {
92
92
  /** The file contents are transferred to the host and unusable here afterwards. */
93
93
  upload: (upload) => call('apps.upload', upload, { transfer: upload.files.map(file => file.content) }),
94
94
  launch: (id) => call('apps.launch', { id }),
95
+ /** The caller's settings of the developer application `id`, as the host holds them. */
96
+ getConfig: (id) => call('apps.getConfig', { id }),
97
+ /** Replaces them with the edited document; the host applies the answer at once. */
98
+ saveConfig: (id, data) => call('apps.saveConfig', { id, data }),
95
99
  },
96
100
  };
97
101
  }
@@ -122,11 +122,43 @@ export interface OsConfig {
122
122
  windowId: string;
123
123
  instanceId: string | null;
124
124
  }
125
+ /** A setting's value as the configuration service holds it. */
126
+ export type SettingValue = string | number | boolean | null;
125
127
  /** What the host tells an app when it connects, and again whenever it changes. */
126
128
  export interface Context {
127
129
  appId: string;
128
130
  host: 'os' | 'dev';
129
131
  osConfig: OsConfig;
132
+ /** The app's own settings, keyed `<group>.<field>` as its manifest declares them. */
133
+ settings?: Record<string, SettingValue>;
134
+ }
135
+ /** One setting as the configuration service renders it: value, flags and declared metadata. */
136
+ export interface Setting {
137
+ value: SettingValue;
138
+ type: string;
139
+ editable: boolean;
140
+ hidden: boolean;
141
+ immutable: boolean;
142
+ declared: boolean;
143
+ origin: string;
144
+ section: string;
145
+ label?: string;
146
+ description?: string;
147
+ unit?: string;
148
+ enum?: SettingValue[];
149
+ min?: number;
150
+ max?: number;
151
+ step?: number;
152
+ order?: number;
153
+ appId?: string;
154
+ }
155
+ /**
156
+ * A developer's own settings of a developer application: `data` is the document they edit
157
+ * (keys `apps.<id>.<group>.<field>`, group flags under `<group>.__flags`), `settings` its view.
158
+ */
159
+ export interface DevAppConfig {
160
+ data: Record<string, unknown>;
161
+ settings: Record<string, Setting>;
130
162
  }
131
163
  export interface IdbEntry<T = unknown> {
132
164
  key: string;
@@ -274,6 +306,13 @@ export declare const APP_METHODS: {
274
306
  }, z.core.$strict>, {
275
307
  windowId: string | null;
276
308
  }>;
309
+ readonly 'apps.getConfig': CallMethod<z.ZodMiniObject<{
310
+ id: z.ZodMiniString<string>;
311
+ }, z.core.$strict>, DevAppConfig>;
312
+ readonly 'apps.saveConfig': CallMethod<z.ZodMiniObject<{
313
+ id: z.ZodMiniString<string>;
314
+ data: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniUnknown>;
315
+ }, z.core.$strict>, DevAppConfig>;
277
316
  };
278
317
  /** Methods a host calls on its app. */
279
318
  export declare const HOST_METHODS: {
package/dist/contract.js CHANGED
@@ -103,6 +103,10 @@ const AppRecord = z.strictObject({
103
103
  bundleSize: z.optional(count),
104
104
  fileCount: z.optional(count),
105
105
  });
106
+ const AppConfigSave = z.strictObject({
107
+ id: text,
108
+ data: z.record(z.string(), z.unknown()),
109
+ });
106
110
  const AppUpload = z.strictObject({
107
111
  alias: text,
108
112
  name: text,
@@ -138,6 +142,8 @@ export const APP_METHODS = {
138
142
  'apps.delete': call(AppId).returns(),
139
143
  'apps.upload': call(AppUpload).returns(120_000),
140
144
  'apps.launch': call(AppId).returns(),
145
+ 'apps.getConfig': call(AppId).returns(),
146
+ 'apps.saveConfig': call(AppConfigSave).returns(),
141
147
  };
142
148
  /** Methods a host calls on its app. */
143
149
  export const HOST_METHODS = {
package/dist/dev-idb.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { AppMethods, IdbEntry, Params } from './contract.js';
2
2
  type In<K extends keyof AppMethods> = Params<AppMethods[K]>;
3
3
  /** The host's app storage, kept in this browser's IndexedDB while running standalone. */
4
- export declare function devIdb(appId: string): {
4
+ export declare function devIdb(): {
5
5
  get: ({ store, key }: In<"idb.get">) => Promise<IdbEntry<unknown> | null>;
6
6
  put: ({ store, key, value }: In<"idb.put">) => Promise<void>;
7
7
  delete: ({ store, key }: In<"idb.delete">) => Promise<void>;
package/dist/dev-idb.js CHANGED
@@ -12,11 +12,11 @@ function open(name) {
12
12
  }
13
13
  const toEntry = ({ key, value, updatedAt }) => ({ key, value, updatedAt });
14
14
  /** The host's app storage, kept in this browser's IndexedDB while running standalone. */
15
- export function devIdb(appId) {
15
+ export function devIdb() {
16
16
  // Opened on first use: most apps never touch their store.
17
17
  let db;
18
18
  const entries = async (mode) => {
19
- db ??= open(`bardioc-dev:${appId}`);
19
+ db ??= open('bardioc-dev');
20
20
  return (await db).transaction(ENTRIES, mode).objectStore(ENTRIES);
21
21
  };
22
22
  return {
package/dist/dev.d.ts CHANGED
@@ -2,17 +2,13 @@ import { type HostHandlers } from './serve.js';
2
2
  export declare function isInsideIframe(): boolean;
3
3
  export declare function isDevStandalone(): boolean;
4
4
  export interface DevHostOptions {
5
- /** The app's manifest id, handed to the app as `context.appId`. */
6
- appId: string;
7
- /** The app's ogit/Auth/Application id, sent as `X-App-ID` on scoped calls; defaults to `appId`. */
8
- clientId?: string;
9
5
  /** Where the dev-auth proxy answers `/rest`. Default: `/rest` on this origin. */
10
6
  apiBaseUrl?: string;
11
7
  }
12
8
  /** The dev host's answers to every app method. */
13
- export declare function devHandlers({ appId, clientId, apiBaseUrl, }: DevHostOptions): HostHandlers;
9
+ export declare function devHandlers({ apiBaseUrl }?: DevHostOptions): HostHandlers;
14
10
  /**
15
11
  * Stands in for the host while the app runs standalone in development: `connect()` reaches it
16
12
  * the way it reaches the host, and BFF calls go through the dev-auth proxy.
17
13
  */
18
- export declare function installDevHost(options: DevHostOptions): () => void;
14
+ export declare function installDevHost(options?: DevHostOptions): () => void;
package/dist/dev.js CHANGED
@@ -23,8 +23,8 @@ const unavailable = () => {
23
23
  throw new BardiocError('unavailable', 'Installed apps live in the Bardioc host');
24
24
  };
25
25
  /** The dev host's answers to every app method. */
26
- export function devHandlers({ appId, clientId = appId, apiBaseUrl = '/rest', }) {
27
- const idb = devIdb(appId);
26
+ export function devHandlers({ apiBaseUrl = '/rest' } = {}) {
27
+ const idb = devIdb();
28
28
  const request = async ({ path, method = 'GET', body, contentType, scopeId, scopePolicy }, signal) => {
29
29
  if (scopePolicy === 'required' && !scopeId) {
30
30
  throw new BardiocError('invalid', 'scopeId is required: a dev session has no instance');
@@ -32,10 +32,9 @@ export function devHandlers({ appId, clientId = appId, apiBaseUrl = '/rest', })
32
32
  const headers = new Headers({ 'X-Requested-With': 'XMLHttpRequest' });
33
33
  if (contentType && body !== undefined)
34
34
  headers.set('Content-Type', contentType);
35
- if (scopeId) {
35
+ // No X-App-ID: a dev app claims no app token and runs on the user's own.
36
+ if (scopeId)
36
37
  headers.set('X-Scope-ID', scopeId);
37
- headers.set('X-App-ID', clientId);
38
- }
39
38
  const encoded = contentType === 'application/json' ? JSON.stringify(body) : body;
40
39
  const response = await fetch(`${apiBaseUrl}${path}`, {
41
40
  method,
@@ -58,6 +57,16 @@ export function devHandlers({ appId, clientId = appId, apiBaseUrl = '/rest', })
58
57
  }
59
58
  return () => source.close();
60
59
  };
60
+ // The BFF's own answer, so a standalone app tries the settings the host would hold.
61
+ const devConfig = async (id, signal, data) => {
62
+ const layer = (await request({
63
+ path: `/os/dev/config/apps/${encodeURIComponent(id)}`,
64
+ method: data ? 'PUT' : 'GET',
65
+ body: data ? { layer: 'dev', data } : undefined,
66
+ contentType: data ? 'application/json' : undefined,
67
+ }, signal));
68
+ return { data: layer.data ?? {}, settings: layer.settings ?? {} };
69
+ };
61
70
  return {
62
71
  'ui.notify': () => undefined,
63
72
  'ui.setCommands': () => undefined,
@@ -76,6 +85,8 @@ export function devHandlers({ appId, clientId = appId, apiBaseUrl = '/rest', })
76
85
  'apps.delete': unavailable,
77
86
  'apps.upload': unavailable,
78
87
  'apps.launch': unavailable,
88
+ 'apps.getConfig': ({ id }, signal) => devConfig(id, signal),
89
+ 'apps.saveConfig': ({ id, data }, signal) => devConfig(id, signal, data),
79
90
  };
80
91
  }
81
92
  const DARK = '(prefers-color-scheme: dark)';
@@ -90,9 +101,9 @@ const osConfig = () => ({
90
101
  * Stands in for the host while the app runs standalone in development: `connect()` reaches it
91
102
  * the way it reaches the host, and BFF calls go through the dev-auth proxy.
92
103
  */
93
- export function installDevHost(options) {
104
+ export function installDevHost(options = {}) {
94
105
  const handlers = devHandlers(options);
95
- const context = () => ({ appId: options.appId, host: 'dev', osConfig: osConfig() });
106
+ const context = () => ({ appId: 'dev', host: 'dev', osConfig: osConfig() });
96
107
  let connection;
97
108
  const stopAccepting = acceptApp({
98
109
  source: () => window,
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { connect } from './client.js';
5
5
  export type { Bardioc } from './client.js';
6
6
  export { BardiocError } from './errors.js';
7
7
  export type { ErrorCode } from './errors.js';
8
- export type { AppRecord, AppUpload, BffRequest, Command, Context, GraphEvent, IdbEntry, InstalledApp, MenuItem, MenuRoot, NotifyLevel, OsConfig, Shortcut, UploadResult, } from './contract.js';
8
+ export type { AppRecord, AppUpload, BffRequest, Command, Context, DevAppConfig, GraphEvent, IdbEntry, InstalledApp, MenuItem, MenuRoot, NotifyLevel, OsConfig, Setting, SettingValue, Shortcut, UploadResult, } from './contract.js';
9
9
  export { DEFAULT_WINDOW_SIZE, MANIFEST_FILENAME, WINDOW_SIZES, validateManifest, } from './manifest.js';
10
10
  export type { AppManifest, AppManifestRole, AppManifestRules, AppManifestWindowConfig, CustomWindowSize, WindowSizePreset, } from './manifest.js';
11
11
  export type { Application, BatchResult, CombinedQueryOptions, Configuration, CreateMemberInput, CreateUnitInput, DeleteOptions, FileEntry, GetNodeOptions, GraphEdgeRaw, GraphNodeRaw, GremlinOptions, HistoryEntry, HistoryOptions, InstanceList, MutationOptions, OrgMember, OrgStructure, OrgUnit, QueryOptions, TimeseriesAggregate, TimeseriesHistory, TimeseriesHistoryOptions, TimeseriesOptions, TimeseriesQueryOptions, TimeseriesValue, TransportScopeOptions, UpdateMemberInput, UpdateUnitInput, UserProfile, } from './types.js';
@@ -1,5 +1,5 @@
1
1
  export { APP_METHODS, GRAPH_EVENT_TYPES, HOST_METHODS, PROTOCOL_VERSION, graphEventFilter, } from './contract.js';
2
- export type { AppMethods, AppRecord, AppUpload, BffRequest, CallMethod, Command, Context, GraphEvent, HostEvents, HostMethods, IdbEntry, InstalledApp, MenuItem, MenuRoot, Method, NotifyLevel, OsConfig, Params, Result, Shortcut, StreamMethod, UploadResult, } from './contract.js';
2
+ export type { AppMethods, AppRecord, AppUpload, BffRequest, CallMethod, Command, Context, DevAppConfig, GraphEvent, HostEvents, HostMethods, IdbEntry, InstalledApp, MenuItem, MenuRoot, Method, NotifyLevel, OsConfig, Params, Result, Setting, SettingValue, Shortcut, StreamMethod, UploadResult, } from './contract.js';
3
3
  export { BardiocError } from './errors.js';
4
4
  export type { ErrorCode, WireError } from './errors.js';
5
5
  export { acceptApp, serve } from './serve.js';
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type DependencyList, type ReactNode } from 'react';
2
2
  import { type Bardioc } from './client.js';
3
- import type { Command, GraphEvent, MenuRoot, NotifyLevel, OsConfig } from './contract.js';
3
+ import type { Command, GraphEvent, MenuRoot, NotifyLevel, OsConfig, SettingValue } from './contract.js';
4
4
  export interface AppSdkProviderProps {
5
5
  children: ReactNode;
6
6
  /** Apply theme, language and size to this document. Default: `true`. */
@@ -21,6 +21,8 @@ export declare function useCommandMenu(commands: AppCommand[], menu: MenuRoot[]
21
21
  export declare function useActiveTab(activeTab: string | null): void;
22
22
  /** The host's current theme, language and size. */
23
23
  export declare function useOsConfig(): OsConfig;
24
+ /** The app's own settings, keyed `<group>.<field>`; follows every change the host pushes. */
25
+ export declare function useSettings(): Record<string, SettingValue>;
24
26
  /** Streams graph events of `types` while mounted; `handler` may change without resubscribing. */
25
27
  export declare function useGraphEvents({ types }: {
26
28
  types: string[];
package/dist/react.js CHANGED
@@ -102,6 +102,17 @@ export function useOsConfig() {
102
102
  }, [bardioc]);
103
103
  return osConfig;
104
104
  }
105
+ /** The app's own settings, keyed `<group>.<field>`; follows every change the host pushes. */
106
+ export function useSettings() {
107
+ const bardioc = useSdk();
108
+ const [settings, setSettings] = useState(bardioc.context.settings ?? NO_SETTINGS);
109
+ useEffect(() => {
110
+ setSettings(bardioc.context.settings ?? NO_SETTINGS);
111
+ return bardioc.onContextChange(context => setSettings(context.settings ?? NO_SETTINGS));
112
+ }, [bardioc]);
113
+ return settings;
114
+ }
115
+ const NO_SETTINGS = {};
105
116
  /** Streams graph events of `types` while mounted; `handler` may change without resubscribing. */
106
117
  export function useGraphEvents({ types }, handler, deps = []) {
107
118
  const { events } = useSdk();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bardioc/app-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "SDK for building apps for Bardioc",
5
5
  "license": "MIT",
6
6
  "private": false,