@iota-uz/sdk 0.6.1 → 0.8.0

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.
Files changed (45) hide show
  1. package/README.md +4 -1
  2. package/dist/client-host/bootstrap.d.ts +41 -0
  3. package/dist/client-host/bootstrap.d.ts.map +1 -0
  4. package/dist/client-host/bootstrap.js +63 -0
  5. package/dist/client-host/draft.d.ts +22 -0
  6. package/dist/client-host/draft.d.ts.map +1 -0
  7. package/dist/client-host/draft.js +73 -0
  8. package/dist/client-host/errors.d.ts.map +1 -1
  9. package/dist/client-host/errors.js +10 -6
  10. package/dist/client-host/identity.d.ts +4 -4
  11. package/dist/client-host/identity.js +2 -2
  12. package/dist/client-host/index.d.ts +3 -0
  13. package/dist/client-host/index.d.ts.map +1 -1
  14. package/dist/client-host/index.js +3 -0
  15. package/dist/client-host/navigation.d.ts +16 -0
  16. package/dist/client-host/navigation.d.ts.map +1 -0
  17. package/dist/client-host/navigation.js +41 -0
  18. package/dist/client-host/route.d.ts +2 -14
  19. package/dist/client-host/route.d.ts.map +1 -1
  20. package/dist/client-host/route.js +11 -24
  21. package/dist/client-host/rpc-core.d.ts +37 -0
  22. package/dist/client-host/rpc-core.d.ts.map +1 -0
  23. package/dist/client-host/rpc-core.js +79 -0
  24. package/dist/client-host/rpc.d.ts +3 -34
  25. package/dist/client-host/rpc.d.ts.map +1 -1
  26. package/dist/client-host/rpc.js +3 -65
  27. package/dist/client-host/services.d.ts +23 -0
  28. package/dist/client-host/services.d.ts.map +1 -0
  29. package/dist/client-host/services.js +57 -0
  30. package/dist/client-host/solid-index.d.ts +13 -0
  31. package/dist/client-host/solid-index.d.ts.map +1 -0
  32. package/dist/client-host/solid-index.js +12 -0
  33. package/dist/client-host/solid-rpc.d.ts +25 -0
  34. package/dist/client-host/solid-rpc.d.ts.map +1 -0
  35. package/dist/client-host/solid-rpc.js +52 -0
  36. package/dist/client-host/solid.d.ts +17 -0
  37. package/dist/client-host/solid.d.ts.map +1 -0
  38. package/dist/client-host/solid.js +77 -0
  39. package/dist/client-host/transport.d.ts +2 -1
  40. package/dist/client-host/transport.d.ts.map +1 -1
  41. package/dist/sdk-identity.cjs +3 -3
  42. package/dist/sdk-identity.js +3 -3
  43. package/dist/sdk-identity.json +2 -2
  44. package/dist/styles.css +1436 -0
  45. package/package.json +19 -2
package/README.md CHANGED
@@ -4,11 +4,14 @@ This is the single public JavaScript distribution for iota-sdk. Its bounded
4
4
  subpath APIs are built from separate internal workspace modules:
5
5
 
6
6
  - `@iota-uz/sdk/client-host`
7
+ - `@iota-uz/sdk/solid`
8
+ - `@iota-uz/sdk/styles.css`
7
9
  - `@iota-uz/sdk/lens`
8
10
  - `@iota-uz/sdk/lens/styles.css`
9
11
  - `@iota-uz/sdk/identity`
10
12
 
11
13
  Version 0.5 is a clean break: legacy root, BiChat, applet, Tailwind, and asset
12
- exports are intentionally absent. React and ReactDOM are peer dependencies;
14
+ exports are intentionally absent. Solid is the standard client-route renderer;
15
+ React and ReactDOM remain optional peers for migration compatibility;
13
16
  Lens implementation dependencies are contained in lazy Lens chunks and are not
14
17
  loaded by client-host imports.
@@ -0,0 +1,41 @@
1
+ export declare const CLIENT_BOOTSTRAP_VERSION = "1.0.0";
2
+ export declare class ClientHostProtocolError extends Error {
3
+ readonly expected: string;
4
+ readonly actual: string;
5
+ constructor(expected: string, actual: string, contract?: string);
6
+ }
7
+ export interface ClientRouteContext<TInitial = unknown> {
8
+ bootstrapVersion?: string;
9
+ protocolVersion: string;
10
+ sdkReleaseVersion: string;
11
+ sdkCommit: string;
12
+ initial: TInitial;
13
+ theme: 'light' | 'dark';
14
+ csrf?: string;
15
+ route?: {
16
+ id: string;
17
+ path: string;
18
+ featureId: string;
19
+ };
20
+ session?: {
21
+ csrf?: string;
22
+ expiresAt?: string;
23
+ refreshEndpoint?: string;
24
+ reauthUrl?: string;
25
+ };
26
+ locale?: {
27
+ language: string;
28
+ messages: Readonly<Record<string, string>>;
29
+ };
30
+ user?: unknown;
31
+ tenant?: unknown;
32
+ permissions?: readonly string[];
33
+ services?: {
34
+ rpc?: string;
35
+ telemetry?: string;
36
+ };
37
+ }
38
+ export declare function assertClientHostProtocol(actual: string): void;
39
+ export declare function assertClientBootstrap(context: unknown, requireVersion?: boolean): asserts context is ClientRouteContext;
40
+ export declare function readClientBootstrap<TInitial = unknown>(document: Document, elementID?: string): ClientRouteContext<TInitial>;
41
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,wBAAwB,UAAU,CAAA;AAE/C,qBAAa,uBAAwB,SAAQ,KAAK;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAzC,QAAQ,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM,EAAE,QAAQ,SAAyB;CAIlG;AAED,MAAM,WAAW,kBAAkB,CAAC,QAAQ,GAAG,OAAO;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACvD,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7F,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;KAAE,CAAA;IACzE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAChD;AAeD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAI7D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,UAAO,GAAG,OAAO,CAAC,OAAO,IAAI,kBAAkB,CAwBpH;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,SAAwB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAM3I"}
@@ -0,0 +1,63 @@
1
+ import { assertSDKIdentity } from './identity';
2
+ import { CLIENT_HOST_PROTOCOL_VERSION } from './protocol';
3
+ export const CLIENT_BOOTSTRAP_VERSION = '1.0.0';
4
+ export class ClientHostProtocolError extends Error {
5
+ expected;
6
+ actual;
7
+ constructor(expected, actual, contract = 'client host protocol') {
8
+ super(`${contract} ${actual} is incompatible with ${expected}`);
9
+ this.expected = expected;
10
+ this.actual = actual;
11
+ this.name = 'ClientHostProtocolError';
12
+ }
13
+ }
14
+ function major(version) { return version.split('.', 1)[0] ?? ''; }
15
+ function invalidBootstrapField(field, value) {
16
+ const actual = value === null ? 'null' : typeof value;
17
+ throw new ClientHostProtocolError('string', actual, `client bootstrap ${field}`);
18
+ }
19
+ function requiredString(context, field) {
20
+ const value = context[field];
21
+ if (typeof value !== 'string')
22
+ invalidBootstrapField(field, value);
23
+ return value;
24
+ }
25
+ export function assertClientHostProtocol(actual) {
26
+ if (major(actual) !== major(CLIENT_HOST_PROTOCOL_VERSION)) {
27
+ throw new ClientHostProtocolError(CLIENT_HOST_PROTOCOL_VERSION, actual);
28
+ }
29
+ }
30
+ export function assertClientBootstrap(context, requireVersion = true) {
31
+ if (!context || typeof context !== 'object' || Array.isArray(context)) {
32
+ throw new ClientHostProtocolError(CLIENT_BOOTSTRAP_VERSION, context === null ? 'null' : typeof context, 'client bootstrap');
33
+ }
34
+ const payload = context;
35
+ const protocolVersion = requiredString(payload, 'protocolVersion');
36
+ const sdkReleaseVersion = requiredString(payload, 'sdkReleaseVersion');
37
+ const sdkCommit = requiredString(payload, 'sdkCommit');
38
+ if (!Object.hasOwn(payload, 'initial'))
39
+ invalidBootstrapField('initial', undefined);
40
+ if (payload.theme !== 'light' && payload.theme !== 'dark')
41
+ invalidBootstrapField('theme', payload.theme);
42
+ if (payload.bootstrapVersion !== undefined && typeof payload.bootstrapVersion !== 'string') {
43
+ invalidBootstrapField('bootstrapVersion', payload.bootstrapVersion);
44
+ }
45
+ assertClientHostProtocol(protocolVersion);
46
+ assertSDKIdentity({
47
+ releaseVersion: sdkReleaseVersion,
48
+ sourceCommit: sdkCommit,
49
+ protocolVersion,
50
+ });
51
+ const bootstrapVersion = payload.bootstrapVersion;
52
+ if ((requireVersion || bootstrapVersion !== undefined) && major(bootstrapVersion ?? '') !== major(CLIENT_BOOTSTRAP_VERSION)) {
53
+ throw new ClientHostProtocolError(CLIENT_BOOTSTRAP_VERSION, bootstrapVersion ?? 'missing', 'client bootstrap');
54
+ }
55
+ }
56
+ export function readClientBootstrap(document, elementID = 'iota-client-context') {
57
+ const element = document.getElementById(elementID);
58
+ if (!element?.textContent)
59
+ throw new ClientHostProtocolError(CLIENT_BOOTSTRAP_VERSION, 'missing', 'client bootstrap');
60
+ const context = JSON.parse(element.textContent);
61
+ assertClientBootstrap(context);
62
+ return context;
63
+ }
@@ -0,0 +1,22 @@
1
+ import { type SetStoreFunction, type Store } from 'solid-js/store';
2
+ import { type HostError } from './errors';
3
+ import type { NavigationService } from './navigation';
4
+ export interface DraftOptions {
5
+ navigation?: NavigationService;
6
+ }
7
+ export declare function createDraft<T extends object>(initial: T, options?: DraftOptions): {
8
+ value: Store<T>;
9
+ set: SetStoreFunction<T>;
10
+ snapshot: () => T;
11
+ baseline: import("solid-js").Accessor<T>;
12
+ dirty: () => boolean;
13
+ pending: import("solid-js").Accessor<boolean>;
14
+ error: import("solid-js").Accessor<HostError | undefined>;
15
+ fieldErrors: () => Readonly<Record<string, string>>;
16
+ accept: (snapshot: T) => void;
17
+ reset: () => void;
18
+ offerServerSnapshot: (snapshot: T) => boolean;
19
+ save: (persist: (snapshot: T, signal: AbortSignal) => Promise<T>) => Promise<boolean>;
20
+ cancelSave: () => void | undefined;
21
+ };
22
+ //# sourceMappingURL=draft.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../src/draft.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,gBAAgB,EAAE,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAElG,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAIrD,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,iBAAiB,CAAA;CAC/B;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAE,YAAiB;WA6DhE,KAAK,CAAC,CAAC,CAAC;;;;;;;;uBArCA,CAAC;;oCAUY,CAAC,KAAG,OAAO;oBAKrB,CAAC,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,OAAO,CAAC;;EA0BjG"}
@@ -0,0 +1,73 @@
1
+ import { createSignal, onCleanup } from 'solid-js';
2
+ import { createStore, reconcile, unwrap } from 'solid-js/store';
3
+ import { stableSerialize } from './cache';
4
+ import { asHostError } from './errors';
5
+ function clone(value) { return structuredClone(unwrap(value)); }
6
+ export function createDraft(initial, options = {}) {
7
+ const [value, setStore] = createStore(clone(initial));
8
+ const [baseline, setBaseline] = createSignal(clone(initial));
9
+ const [revision, setRevision] = createSignal(0);
10
+ const [pending, setPending] = createSignal(false);
11
+ const [error, setError] = createSignal();
12
+ let saveGeneration = 0;
13
+ let saveController;
14
+ const dirty = () => {
15
+ revision();
16
+ return stableSerialize(value) !== stableSerialize(baseline());
17
+ };
18
+ const releaseGuard = options.navigation?.guard(() => !dirty());
19
+ onCleanup(() => {
20
+ saveController?.abort();
21
+ releaseGuard?.();
22
+ });
23
+ const set = ((...args) => {
24
+ setRevision((current) => current + 1);
25
+ setError(undefined);
26
+ setStore(...args);
27
+ });
28
+ const accept = (snapshot) => {
29
+ const next = clone(snapshot);
30
+ setStore(reconcile(next));
31
+ setBaseline(() => clone(next));
32
+ setRevision((current) => current + 1);
33
+ setError(undefined);
34
+ };
35
+ const reset = () => accept(baseline());
36
+ const snapshot = () => clone(value);
37
+ const offerServerSnapshot = (snapshot) => {
38
+ if (dirty())
39
+ return false;
40
+ accept(snapshot);
41
+ return true;
42
+ };
43
+ const save = async (persist) => {
44
+ const currentGeneration = ++saveGeneration;
45
+ const startRevision = revision();
46
+ const snapshot = clone(value);
47
+ saveController?.abort();
48
+ saveController = new AbortController();
49
+ setPending(true);
50
+ setError(undefined);
51
+ try {
52
+ const saved = await persist(snapshot, saveController.signal);
53
+ if (currentGeneration !== saveGeneration || saveController.signal.aborted || revision() !== startRevision)
54
+ return false;
55
+ accept(saved);
56
+ return true;
57
+ }
58
+ catch (cause) {
59
+ if (currentGeneration === saveGeneration && !saveController.signal.aborted)
60
+ setError(asHostError(cause));
61
+ throw cause;
62
+ }
63
+ finally {
64
+ if (currentGeneration === saveGeneration)
65
+ setPending(false);
66
+ }
67
+ };
68
+ return {
69
+ value: value, set, snapshot, baseline, dirty, pending, error,
70
+ fieldErrors: () => error()?.fieldErrors ?? {}, accept, reset, offerServerSnapshot, save,
71
+ cancelSave: () => saveController?.abort(),
72
+ };
73
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB,iBAAiB,GACjB,mBAAmB,GACnB,YAAY,GACZ,kBAAkB,GAClB,WAAW,GACX,UAAU,GACV,SAAS,CAAA;AAEb,qBAAa,SAAU,SAAQ,KAAK;IAEhC,QAAQ,CAAC,IAAI,EAAE,aAAa;IAE5B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;IAC1B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAH7C,IAAI,EAAE,aAAa,EAC5B,OAAO,EAAE,MAAM,EACN,OAAO,CAAC,EAAE,OAAO,YAAA,EACjB,WAAW,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAM7D,IAAI,SAAS,IAAI,OAAO,CAAqC;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACxC,gBAAgB,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACzC,UAAU,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;CACpC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAsBrD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAItF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB,iBAAiB,GACjB,mBAAmB,GACnB,YAAY,GACZ,kBAAkB,GAClB,WAAW,GACX,UAAU,GACV,SAAS,CAAA;AAEb,qBAAa,SAAU,SAAQ,KAAK;IAIhC,QAAQ,CAAC,IAAI,EAAE,aAAa;IAE5B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;IAL5B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;gBAG3C,IAAI,EAAE,aAAa,EAC5B,OAAO,EAAE,MAAM,EACN,OAAO,CAAC,EAAE,OAAO,YAAA,EAC1B,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAWhD,IAAI,SAAS,IAAI,OAAO,CAAqC;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACxC,gBAAgB,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACzC,UAAU,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;CACpC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAwBrD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAItF"}
@@ -2,12 +2,14 @@ export class HostError extends Error {
2
2
  code;
3
3
  details;
4
4
  fieldErrors;
5
- constructor(code, message, details, fieldErrors = {}) {
5
+ constructor(code, message, details, fieldErrors) {
6
6
  super(message);
7
7
  this.code = code;
8
8
  this.details = details;
9
- this.fieldErrors = fieldErrors;
10
9
  this.name = 'HostError';
10
+ this.fieldErrors = fieldErrors ?? (code === 'field_validation' && details && typeof details === 'object'
11
+ ? details
12
+ : {});
11
13
  }
12
14
  get retryable() { return this.code === 'transient'; }
13
15
  }
@@ -15,7 +17,9 @@ export function asHostError(input) {
15
17
  if (input instanceof HostError)
16
18
  return input;
17
19
  const candidate = input;
18
- const raw = String(candidate?.code ?? 'unknown');
20
+ const details = candidate?.details ?? candidate?.data;
21
+ const detailRecord = details && typeof details === 'object' ? details : undefined;
22
+ const raw = String(detailRecord?.code ?? candidate?.code ?? 'unknown');
19
23
  const code = raw === 'unauthenticated' || raw === 'unauthorized'
20
24
  ? 'unauthenticated'
21
25
  : raw === 'forbidden' || raw === 'permission_denied'
@@ -29,9 +33,9 @@ export function asHostError(input) {
29
33
  : raw === 'protocol'
30
34
  ? 'protocol'
31
35
  : 'unknown';
32
- const details = candidate?.details;
33
- const fieldErrors = code === 'field_validation' && details && typeof details === 'object'
34
- ? details
36
+ const fields = detailRecord?.fieldErrors ?? detailRecord?.fields ?? details;
37
+ const fieldErrors = code === 'field_validation' && fields && typeof fields === 'object'
38
+ ? fields
35
39
  : {};
36
40
  return new HostError(code, String(candidate?.message ?? input ?? 'Unknown host error'), details, fieldErrors);
37
41
  }
@@ -1,9 +1,9 @@
1
- export declare const SDK_RELEASE_VERSION = "0.6.1";
2
- export declare const SDK_SOURCE_COMMIT = "c8cac3d20d74aa31afb6b1f915b2701e943c6b4b";
1
+ export declare const SDK_RELEASE_VERSION = "0.8.0";
2
+ export declare const SDK_SOURCE_COMMIT = "4db430f807b23db5df725e623b23b45ccb30c9db";
3
3
  export declare const SDK_PROTOCOL_VERSION = "1.0.0";
4
4
  export declare const SDK_IDENTITY: Readonly<{
5
- releaseVersion: "0.6.1";
6
- sourceCommit: "c8cac3d20d74aa31afb6b1f915b2701e943c6b4b";
5
+ releaseVersion: "0.8.0";
6
+ sourceCommit: "4db430f807b23db5df725e623b23b45ccb30c9db";
7
7
  protocolVersion: "1.0.0";
8
8
  }>;
9
9
  export interface SDKRuntimeIdentity {
@@ -1,6 +1,6 @@
1
1
  import { CLIENT_HOST_PROTOCOL_VERSION } from './protocol';
2
- export const SDK_RELEASE_VERSION = '0.6.1';
3
- export const SDK_SOURCE_COMMIT = 'c8cac3d20d74aa31afb6b1f915b2701e943c6b4b';
2
+ export const SDK_RELEASE_VERSION = '0.8.0';
3
+ export const SDK_SOURCE_COMMIT = '4db430f807b23db5df725e623b23b45ccb30c9db';
4
4
  export const SDK_PROTOCOL_VERSION = CLIENT_HOST_PROTOCOL_VERSION;
5
5
  export const SDK_IDENTITY = Object.freeze({
6
6
  releaseVersion: SDK_RELEASE_VERSION,
@@ -1,11 +1,14 @@
1
1
  export * from './applet';
2
+ export * from './bootstrap';
2
3
  export * from './cache';
3
4
  export * from './errors';
4
5
  export * from './identity';
6
+ export * from './navigation';
5
7
  export * from './portals';
6
8
  export * from './protocol';
7
9
  export * from './route';
8
10
  export * from './rpc';
11
+ export * from './services';
9
12
  export * from './streaming';
10
13
  export * from './transport';
11
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA;AACrB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA;AACrB,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
@@ -1,10 +1,13 @@
1
1
  export * from './applet';
2
+ export * from './bootstrap';
2
3
  export * from './cache';
3
4
  export * from './errors';
4
5
  export * from './identity';
6
+ export * from './navigation';
5
7
  export * from './portals';
6
8
  export * from './protocol';
7
9
  export * from './route';
8
10
  export * from './rpc';
11
+ export * from './services';
9
12
  export * from './streaming';
10
13
  export * from './transport';
@@ -0,0 +1,16 @@
1
+ export type NavigationGuard = () => boolean;
2
+ export interface NavigationService {
3
+ guard(check: NavigationGuard): () => void;
4
+ canNavigate(): boolean;
5
+ }
6
+ export declare class BrowserNavigationService implements NavigationService {
7
+ private readonly owner;
8
+ private readonly guards;
9
+ private readonly beforeUnload;
10
+ private readonly click;
11
+ constructor(owner?: Window);
12
+ guard(check: NavigationGuard): () => void;
13
+ canNavigate(): boolean;
14
+ dispose(): void;
15
+ }
16
+ //# sourceMappingURL=navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAA;AAE3C,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,IAAI,CAAA;IACzC,WAAW,IAAI,OAAO,CAAA;CACvB;AAED,qBAAa,wBAAyB,YAAW,iBAAiB;IAgBpD,OAAO,CAAC,QAAQ,CAAC,KAAK;IAflC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAI5B;IACD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAOrB;gBAE4B,KAAK,GAAE,MAAe;IAKnD,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,IAAI;IAKzC,WAAW,IAAI,OAAO;IAKtB,OAAO,IAAI,IAAI;CAKhB"}
@@ -0,0 +1,41 @@
1
+ export class BrowserNavigationService {
2
+ owner;
3
+ guards = new Set();
4
+ beforeUnload = (event) => {
5
+ if (this.canNavigate())
6
+ return;
7
+ event.preventDefault();
8
+ event.returnValue = '';
9
+ };
10
+ click = (event) => {
11
+ const eventTarget = event.target;
12
+ const target = typeof eventTarget?.closest === 'function' ? eventTarget.closest('a[href]') : null;
13
+ if (!target || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
14
+ return;
15
+ const anchor = target;
16
+ if (anchor.target === '_blank' || anchor.download || anchor.origin !== this.owner.location.origin)
17
+ return;
18
+ if (!this.canNavigate())
19
+ event.preventDefault();
20
+ };
21
+ constructor(owner = window) {
22
+ this.owner = owner;
23
+ owner.addEventListener('beforeunload', this.beforeUnload);
24
+ owner.document.addEventListener('click', this.click, true);
25
+ }
26
+ guard(check) {
27
+ this.guards.add(check);
28
+ return () => this.guards.delete(check);
29
+ }
30
+ canNavigate() {
31
+ for (const check of this.guards)
32
+ if (!check())
33
+ return false;
34
+ return true;
35
+ }
36
+ dispose() {
37
+ this.owner.removeEventListener('beforeunload', this.beforeUnload);
38
+ this.owner.document.removeEventListener('click', this.click, true);
39
+ this.guards.clear();
40
+ }
41
+ }
@@ -1,18 +1,7 @@
1
1
  import type { ComponentType } from 'react';
2
2
  import { type WidgetSlotName } from './portals';
3
- export declare class ClientHostProtocolError extends Error {
4
- readonly expected: string;
5
- readonly actual: string;
6
- constructor(expected: string, actual: string);
7
- }
8
- export interface ClientRouteContext<TInitial = unknown> {
9
- protocolVersion: string;
10
- sdkReleaseVersion: string;
11
- sdkCommit: string;
12
- initial: TInitial;
13
- theme: 'light' | 'dark';
14
- csrf?: string;
15
- }
3
+ import { type ClientRouteContext } from './bootstrap';
4
+ export { ClientHostProtocolError, assertClientHostProtocol, type ClientRouteContext } from './bootstrap';
16
5
  export interface MountClientRouteOptions<TInitial, TProps extends object> {
17
6
  root: HTMLElement;
18
7
  portals: HTMLElement;
@@ -24,6 +13,5 @@ export interface MountClientRouteOptions<TInitial, TProps extends object> {
24
13
  props: TProps;
25
14
  context: ClientRouteContext<TInitial>;
26
15
  }
27
- export declare function assertClientHostProtocol(actual: string): void;
28
16
  export declare function mountClientRoute<TInitial, TProps extends object>(options: MountClientRouteOptions<TInitial, TProps>): () => void;
29
17
  //# sourceMappingURL=route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../src/route.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AAInE,qBAAa,uBAAwB,SAAQ,KAAK;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAzC,QAAQ,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM;CAI/D;AAED,MAAM,WAAW,kBAAkB,CAAC,QAAQ,GAAG,OAAO;IACpD,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,uBAAuB,CAAC,QAAQ,EAAE,MAAM,SAAS,MAAM;IACtE,IAAI,EAAE,WAAW,CAAA;IACjB,OAAO,EAAE,WAAW,CAAA;IACpB,UAAU,CAAC,EAAE,WAAW,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAA;IAC1D,SAAS,EAAE,aAAa,CAAC,MAAM,GAAG;QAAE,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC,CAAA;IAC1E,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAA;CACtC;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAI7D;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,SAAS,MAAM,EAAE,OAAO,EAAE,uBAAuB,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,IAAI,CAehI"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../src/route.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AACnE,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAE5E,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAExG,MAAM,WAAW,uBAAuB,CAAC,QAAQ,EAAE,MAAM,SAAS,MAAM;IACtE,IAAI,EAAE,WAAW,CAAA;IACjB,OAAO,EAAE,WAAW,CAAA;IACpB,UAAU,CAAC,EAAE,WAAW,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAA;IAC1D,SAAS,EAAE,aAAa,CAAC,MAAM,GAAG;QAAE,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC,CAAA;IAC1E,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAA;CACtC;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,SAAS,MAAM,EAAE,OAAO,EAAE,uBAAuB,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,IAAI,CAgBhI"}
@@ -1,32 +1,19 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createRoot } from 'react-dom/client';
3
3
  import { ClientHostProvider } from './portals';
4
- import { assertSDKIdentity } from './identity';
5
- import { CLIENT_HOST_PROTOCOL_VERSION } from './protocol';
6
- export class ClientHostProtocolError extends Error {
7
- expected;
8
- actual;
9
- constructor(expected, actual) {
10
- super(`Client host protocol ${actual} is incompatible with ${expected}`);
11
- this.expected = expected;
12
- this.actual = actual;
13
- this.name = 'ClientHostProtocolError';
14
- }
15
- }
16
- export function assertClientHostProtocol(actual) {
17
- if (actual.split('.', 1)[0] !== CLIENT_HOST_PROTOCOL_VERSION.split('.', 1)[0]) {
18
- throw new ClientHostProtocolError(CLIENT_HOST_PROTOCOL_VERSION, actual);
19
- }
20
- }
4
+ import { assertClientBootstrap } from './bootstrap';
5
+ export { ClientHostProtocolError, assertClientHostProtocol } from './bootstrap';
21
6
  export function mountClientRoute(options) {
22
- assertClientHostProtocol(options.context.protocolVersion);
23
- assertSDKIdentity({
24
- releaseVersion: options.context.sdkReleaseVersion,
25
- sourceCommit: options.context.sdkCommit,
26
- protocolVersion: options.context.protocolVersion,
27
- });
7
+ assertClientBootstrap(options.context, false);
8
+ const currentOwner = options.root.dataset.iotaClientOwner;
9
+ if (currentOwner)
10
+ throw new Error(`Client route root is already owned by ${currentOwner}`);
11
+ options.root.dataset.iotaClientOwner = 'react';
28
12
  const root = createRoot(options.root);
29
13
  const Component = options.component;
30
14
  root.render(_jsx(ClientHostProvider, { portalOwner: options.portals, background: options.background, widgetSlots: options.widgetSlots, theme: options.context.theme, children: _jsx(Component, { ...options.props, route: options.context }) }));
31
- return () => root.unmount();
15
+ return () => {
16
+ root.unmount();
17
+ delete options.root.dataset.iotaClientOwner;
18
+ };
32
19
  }
@@ -0,0 +1,37 @@
1
+ import { type QueryCache, type QueryKey } from './cache';
2
+ import { HostError, type HostErrorHandlers } from './errors';
3
+ export interface RPCMethodContract {
4
+ namespace: string;
5
+ method: string;
6
+ kind: 'query' | 'mutation';
7
+ cacheable?: boolean;
8
+ maxRetries?: number;
9
+ invalidates?: readonly string[];
10
+ }
11
+ export interface RPCTransport {
12
+ call<TRequest, TResponse>(method: string, request: TRequest, signal: AbortSignal): Promise<TResponse>;
13
+ }
14
+ export interface RPCDiagnostics {
15
+ call?(event: {
16
+ method: string;
17
+ key?: QueryKey;
18
+ state: 'start' | 'success' | 'error' | 'cancelled';
19
+ error?: HostError;
20
+ }): void;
21
+ }
22
+ export interface RPCClientOptions {
23
+ cache?: QueryCache;
24
+ errors?: HostErrorHandlers;
25
+ diagnostics?: RPCDiagnostics;
26
+ }
27
+ export declare class RPCClient {
28
+ readonly transport: RPCTransport;
29
+ readonly options: RPCClientOptions;
30
+ readonly cache: QueryCache;
31
+ private queryGeneration;
32
+ private readonly latestQueryGenerations;
33
+ constructor(transport: RPCTransport, options?: RPCClientOptions);
34
+ query<TRequest, TResponse>(contract: RPCMethodContract, request: TRequest, signal: AbortSignal): Promise<TResponse>;
35
+ mutate<TRequest, TResponse>(contract: RPCMethodContract, request: TRequest, signal: AbortSignal): Promise<TResponse>;
36
+ }
37
+ //# sourceMappingURL=rpc-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-core.d.ts","sourceRoot":"","sources":["../src/rpc-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAA;AACpF,OAAO,EAAkC,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE5F,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,OAAO,GAAG,UAAU,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;CACtG;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAAC;QAAC,KAAK,CAAC,EAAE,SAAS,CAAA;KAAE,GAAG,IAAI,CAAA;CAC9H;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,WAAW,CAAC,EAAE,cAAc,CAAA;CAC7B;AAED,qBAAa,SAAS;IAIR,QAAQ,CAAC,SAAS,EAAE,YAAY;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB;IAHhF,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;IAC1B,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAA4B;gBAC9C,SAAS,EAAE,YAAY,EAAW,OAAO,GAAE,gBAAqB;IAI/E,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC;IA0CnH,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC;CAe3H"}
@@ -0,0 +1,79 @@
1
+ import { MemoryQueryCache, queryKey } from './cache';
2
+ import { asHostError, dispatchHostError, HostError } from './errors';
3
+ export class RPCClient {
4
+ transport;
5
+ options;
6
+ cache;
7
+ queryGeneration = 0;
8
+ latestQueryGenerations = new Map();
9
+ constructor(transport, options = {}) {
10
+ this.transport = transport;
11
+ this.options = options;
12
+ this.cache = options.cache ?? new MemoryQueryCache();
13
+ }
14
+ async query(contract, request, signal) {
15
+ if (contract.kind !== 'query')
16
+ throw new HostError('protocol', `${contract.method} is not a query`);
17
+ const key = queryKey(contract.namespace, contract.method, request);
18
+ if (contract.cacheable !== false) {
19
+ const cached = this.cache.get(key);
20
+ if (cached !== undefined)
21
+ return cached;
22
+ }
23
+ const encodedKey = JSON.stringify(key);
24
+ const generation = ++this.queryGeneration;
25
+ this.latestQueryGenerations.set(encodedKey, generation);
26
+ let attempt = 0;
27
+ const configuredRetries = contract.maxRetries ?? 2;
28
+ const maxRetries = Number.isFinite(configuredRetries)
29
+ ? Math.max(0, Math.min(3, Math.trunc(configuredRetries)))
30
+ : 2;
31
+ try {
32
+ for (;;) {
33
+ this.options.diagnostics?.call?.({ method: contract.method, key, state: 'start' });
34
+ try {
35
+ const response = await this.transport.call(contract.method, request, signal);
36
+ if (contract.cacheable !== false && this.latestQueryGenerations.get(encodedKey) === generation)
37
+ this.cache.set(key, response);
38
+ this.options.diagnostics?.call?.({ method: contract.method, key, state: 'success' });
39
+ return response;
40
+ }
41
+ catch (cause) {
42
+ if (signal.aborted) {
43
+ this.options.diagnostics?.call?.({ method: contract.method, key, state: 'cancelled' });
44
+ throw cause;
45
+ }
46
+ const error = asHostError(cause);
47
+ if (!error.retryable || attempt >= maxRetries) {
48
+ dispatchHostError(error, this.options.errors);
49
+ this.options.diagnostics?.call?.({ method: contract.method, key, state: 'error', error });
50
+ throw error;
51
+ }
52
+ attempt += 1;
53
+ }
54
+ }
55
+ }
56
+ finally {
57
+ if (this.latestQueryGenerations.get(encodedKey) === generation)
58
+ this.latestQueryGenerations.delete(encodedKey);
59
+ }
60
+ }
61
+ async mutate(contract, request, signal) {
62
+ if (contract.kind !== 'mutation')
63
+ throw new HostError('protocol', `${contract.method} is not a mutation`);
64
+ this.options.diagnostics?.call?.({ method: contract.method, state: 'start' });
65
+ try {
66
+ const response = await this.transport.call(contract.method, request, signal);
67
+ this.cache.invalidate(contract.namespace, contract.invalidates ?? []);
68
+ this.options.diagnostics?.call?.({ method: contract.method, state: 'success' });
69
+ return response;
70
+ }
71
+ catch (cause) {
72
+ const error = asHostError(cause);
73
+ if (!signal.aborted)
74
+ dispatchHostError(error, this.options.errors);
75
+ this.options.diagnostics?.call?.({ method: contract.method, state: signal.aborted ? 'cancelled' : 'error', error });
76
+ throw error;
77
+ }
78
+ }
79
+ }
@@ -1,37 +1,6 @@
1
- import { type QueryCache, type QueryKey } from './cache';
2
- import { HostError, type HostErrorHandlers } from './errors';
3
- export interface RPCMethodContract {
4
- namespace: string;
5
- method: string;
6
- kind: 'query' | 'mutation';
7
- cacheable?: boolean;
8
- maxRetries?: number;
9
- invalidates?: readonly string[];
10
- }
11
- export interface RPCTransport {
12
- call<TRequest, TResponse>(method: string, request: TRequest, signal: AbortSignal): Promise<TResponse>;
13
- }
14
- export interface RPCDiagnostics {
15
- call?(event: {
16
- method: string;
17
- key?: QueryKey;
18
- state: 'start' | 'success' | 'error' | 'cancelled';
19
- error?: HostError;
20
- }): void;
21
- }
22
- export interface RPCClientOptions {
23
- cache?: QueryCache;
24
- errors?: HostErrorHandlers;
25
- diagnostics?: RPCDiagnostics;
26
- }
27
- export declare class RPCClient {
28
- readonly transport: RPCTransport;
29
- readonly options: RPCClientOptions;
30
- readonly cache: QueryCache;
31
- constructor(transport: RPCTransport, options?: RPCClientOptions);
32
- query<TRequest, TResponse>(contract: RPCMethodContract, request: TRequest, signal: AbortSignal): Promise<TResponse>;
33
- mutate<TRequest, TResponse>(contract: RPCMethodContract, request: TRequest, signal: AbortSignal): Promise<TResponse>;
34
- }
1
+ import { type HostError } from './errors';
2
+ import { RPCClient, type RPCMethodContract } from './rpc-core';
3
+ export * from './rpc-core';
35
4
  export type QueryState<T> = {
36
5
  status: 'loading';
37
6
  } | {