@aidc-toolkit/core 1.0.32-beta → 1.0.34-beta

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 (69) hide show
  1. package/README.md +99 -92
  2. package/dist/app-data-storage.d.ts +118 -0
  3. package/dist/app-data-storage.d.ts.map +1 -0
  4. package/dist/app-data-storage.js +117 -0
  5. package/dist/app-data-storage.js.map +1 -0
  6. package/dist/app-data.d.ts +26 -0
  7. package/dist/app-data.d.ts.map +1 -0
  8. package/dist/app-data.js +79 -0
  9. package/dist/app-data.js.map +1 -0
  10. package/dist/browser-app-data-storage.d.ts +26 -0
  11. package/dist/browser-app-data-storage.d.ts.map +1 -0
  12. package/dist/browser-app-data-storage.js +34 -0
  13. package/dist/browser-app-data-storage.js.map +1 -0
  14. package/dist/file-app-data-storage.d.ts +26 -0
  15. package/dist/file-app-data-storage.d.ts.map +1 -0
  16. package/dist/file-app-data-storage.js +40 -0
  17. package/dist/file-app-data-storage.js.map +1 -0
  18. package/dist/index.d.ts +7 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +22 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/local-app-data-storage.d.ts +8 -0
  23. package/dist/local-app-data-storage.d.ts.map +1 -0
  24. package/dist/local-app-data-storage.js +11 -0
  25. package/dist/local-app-data-storage.js.map +1 -0
  26. package/dist/locale/en/locale-resources.d.ts +3 -0
  27. package/dist/locale/en/locale-resources.d.ts.map +1 -1
  28. package/dist/locale/en/locale-resources.js +3 -0
  29. package/dist/locale/en/locale-resources.js.map +1 -1
  30. package/dist/locale/fr/locale-resources.d.ts +3 -0
  31. package/dist/locale/fr/locale-resources.d.ts.map +1 -1
  32. package/dist/locale/fr/locale-resources.js +3 -0
  33. package/dist/locale/fr/locale-resources.js.map +1 -1
  34. package/dist/locale/i18n.d.ts +22 -13
  35. package/dist/locale/i18n.d.ts.map +1 -1
  36. package/dist/locale/i18n.js +62 -43
  37. package/dist/locale/i18n.js.map +1 -1
  38. package/dist/logger.d.ts +4 -2
  39. package/dist/logger.d.ts.map +1 -1
  40. package/dist/logger.js +9 -3
  41. package/dist/logger.js.map +1 -1
  42. package/dist/parse-version.d.ts +36 -0
  43. package/dist/parse-version.d.ts.map +1 -0
  44. package/dist/parse-version.js +23 -0
  45. package/dist/parse-version.js.map +1 -0
  46. package/dist/phase-url.d.ts +17 -0
  47. package/dist/phase-url.d.ts.map +1 -0
  48. package/dist/phase-url.js +39 -0
  49. package/dist/phase-url.js.map +1 -0
  50. package/dist/remote-app-data-storage.d.ts +18 -0
  51. package/dist/remote-app-data-storage.d.ts.map +1 -0
  52. package/dist/remote-app-data-storage.js +37 -0
  53. package/dist/remote-app-data-storage.js.map +1 -0
  54. package/dist/type-helper.js.map +1 -1
  55. package/package.json +11 -8
  56. package/src/app-data-storage.ts +166 -0
  57. package/src/app-data.ts +94 -0
  58. package/src/browser-app-data-storage.ts +37 -0
  59. package/src/file-app-data-storage.ts +49 -0
  60. package/src/index.ts +10 -1
  61. package/src/local-app-data-storage.ts +12 -0
  62. package/src/locale/en/locale-resources.ts +3 -0
  63. package/src/locale/fr/locale-resources.ts +3 -0
  64. package/src/locale/i18n.ts +81 -49
  65. package/src/logger.ts +10 -3
  66. package/src/parse-version.ts +54 -0
  67. package/src/phase-url.ts +42 -0
  68. package/src/remote-app-data-storage.ts +38 -0
  69. package/src/type-helper.ts +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Core Package
2
2
 
3
- **Copyright © 2024-2025 Dolphin Data Development Ltd. and AIDC Toolkit contributors**
3
+ **Copyright © 2024-2026 Dolphin Data Development Ltd. and AIDC Toolkit contributors**
4
4
 
5
5
  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
6
6
 
@@ -10,15 +10,15 @@ Unless required by applicable law or agreed to in writing, software distributed
10
10
 
11
11
  ## Overview
12
12
 
13
- > [!WARNING]
14
- >
15
- > **This software is in beta**, with production release is scheduled for 2025Q4. To follow the status of this and other projects, go to the AIDC Toolkit [projects](https://github.com/orgs/aidc-toolkit/projects) page.
16
-
17
13
  The AIDC Toolkit `core` package contains artefacts to support other AIDC Toolkit packages; it does not itself provide any of the functionality of the AIDC Toolkit. It is a required dependency for all AIDC Toolkit packages.
18
14
 
19
- ## Types
15
+ ## Types and Type Helpers
16
+
17
+ Generic types that go beyond TypeScript's utility types are defined here. The types are designed to provide even greater type safety. There are also helper functions for working with objects (e.g., by picking or omitting properties).
20
18
 
21
- Generic types that go beyond TypeScript's utility types are defined here. The types are designed to provide even greater type safety.
19
+ ## Hyperlink
20
+
21
+ A simple interface defining a hyperlink is defined in this package to provide a consistent type across all AIDC Toolkit packages.
22
22
 
23
23
  ## Logger
24
24
 
@@ -30,38 +30,38 @@ All AIDC Toolkit packages require internationalization. The localization functio
30
30
 
31
31
  > [!TIP]
32
32
  >
33
- > For a complete example, including how to use application-specific resource bundles, see the AIDC Toolkit [demo source](https://github.com/aidc-toolkit/demo).
33
+ > For a complete example, including how to use application-specific resource bundles, see the AIDC Toolkit [demo source](https://github.com/aidc-toolkit/demo/tree/main/src/locale).
34
34
 
35
- Packages install their resources as follows in `i18n.ts` or similar:
35
+ Packages install their resources as follows in `i18n.ts` or similar. Note that "dependency1" and "dependency2" are placeholders for the names of the packages on which the package depends, and "package" is the package itself.
36
36
 
37
37
  ```typescript
38
- import { type I18nEnvironment, i18nFinalizeInit } from "@aidc-toolkit/core";
39
- import { dependency1Resources, i18nDependency1Init } from "@aidc-toolkit/dependency1";
40
- import { dependency2Resources, i18nDependency2Init } from "@aidc-toolkit/dependency2";
38
+ import { i18nCoreInit, type I18nEnvironment, i18nInit } from "@aidc-toolkit/core";
39
+ import { i18nDependency1Init } from "@aidc-toolkit/dependency1";
40
+ import { i18nDependency2Init } from "@aidc-toolkit/dependency2";
41
41
  import i18next, { type i18n, type Resource } from "i18next";
42
- import { localeStrings as enLocaleStrings } from "./en/locale-strings";
43
- import { localeStrings as frLocaleStrings } from "./fr/locale-strings";
42
+ import enLocaleResources from "./en/locale-resources.js";
43
+ import frLocaleResources from "./fr/locale-resources.js";
44
44
 
45
- export const packageNS = "aidct_package";
45
+ const packageNS = "aidct_package";
46
46
 
47
47
  /**
48
48
  * Locale strings type is extracted from the English locale strings object.
49
49
  */
50
- export type PackageLocaleStrings = typeof enLocaleStrings;
50
+ export type PackageLocaleResources = typeof enLocaleResources;
51
51
 
52
52
  /**
53
- * Package resources.
53
+ * Package resource bundle.
54
54
  */
55
- export const packageResources: Resource = {
56
- en: {
57
- aidct_package: enLocaleStrings
58
- },
59
- fr: {
60
- aidct_package: frLocaleStrings
61
- }
55
+ const packageResourceBundle = {
56
+ en: {
57
+ aidct_package: enLocaleResources
58
+ },
59
+ fr: {
60
+ aidct_package: frLocaleResources
61
+ }
62
62
  };
63
63
 
64
- // Explicit type is necessary to work around bug in type discovery with linked packages.
64
+ // Explicit type is necessary because type can't be inferred without additional references.
65
65
  export const i18nextPackage: i18n = i18next.createInstance();
66
66
 
67
67
  /**
@@ -74,18 +74,17 @@ export const i18nextPackage: i18n = i18next.createInstance();
74
74
  * Debug setting.
75
75
  *
76
76
  * @returns
77
- * Void promise.
77
+ * Package resource bundle.
78
78
  */
79
- export async function i18nPackageInit(environment: I18nEnvironment, debug = false): Promise<void> {
80
- await i18nDependency1Init(environment, debug);
81
- await i18nDependency2Init(environment, debug);
82
- await i18nFinalizeInit(i18nextPackage, environment, debug, packageNS, dependency1Resources, dependency2Resources, packageResources);
79
+ export async function i18nPackageInit(environment: I18nEnvironment, debug = false): Promise<Resource> {
80
+ return i18nInit(i18nextPackage, environment, debug, packageNS, packageResourceBundle, i18nCoreInit, i18nDependency2Init, i18nDependency1Init);
83
81
  }
84
82
  ```
85
83
 
86
84
  The resource types are declared in `i18next.d.ts` or similar:
87
85
 
88
86
  ```typescript
87
+ import type { CoreLocaleResources } from "@aidc-toolkit/core";
89
88
  import type { Dependency1LocaleStrings } from "@aidc-toolkit/dependency1";
90
89
  import type { Dependency2LocaleStrings } from "@aidc-toolkit/dependency2";
91
90
  import type { PackageLocaleStrings } from "./i18n";
@@ -100,6 +99,7 @@ declare module "i18next" {
100
99
  interface CustomTypeOptions {
101
100
  defaultNS: "aidct_package";
102
101
  resources: {
102
+ aidct_core: CoreLocaleResources;
103
103
  aidct_dependency1: Dependency1LocaleStrings;
104
104
  aidct_dependency2: Dependency2LocaleStrings;
105
105
  aidct_package: PackageLocaleStrings;
@@ -108,11 +108,13 @@ declare module "i18next" {
108
108
  }
109
109
  ```
110
110
 
111
+ The declaration in `i18next.d.ts` exposes the resources of the dependencies to the package. The initialization process merges all the resources into a single resource bundle matching the declaration.
112
+
111
113
  Support is available for the following environments:
112
114
 
113
115
  - [Command-line interface](#command-line-interface)
114
116
  - Unit tests
115
- - Batch applications
117
+ - Command-line or helper applications
116
118
  - Web server - **NOT YET IMPLEMENTED**
117
119
  - [Web browser](#web-browser)
118
120
 
@@ -126,73 +128,78 @@ await i18nPackageInit(I18nEnvironment.CLI);
126
128
 
127
129
  ### Web Browser
128
130
 
129
- Initializing internationalization for a web browser requires awaiting the fulfillment of the `Promise` returned by the call to the initialization function before rendering any content. For example, in the React framework, this may be accomplished with a component like this:
131
+ Initializing internationalization for a web browser requires awaiting the fulfillment of the `Promise` returned by the call to the initialization function before rendering any content. For example, in the React framework, this would be done before creating the root:
130
132
 
131
133
  ```typescript jsx
132
- import { I18nEnvironment } from "@aidc-toolkit/core";
133
- import { type ReactElement, type ReactNode, useEffect, useState } from "react";
134
- import { i18nPackageInit, i18nextPackage } from "./locale/i18n";
134
+ import { I18nEnvironments } from "@aidc-toolkit/core";
135
+ import { StrictMode } from "react";
136
+ import { createRoot } from "react-dom/client";
137
+ import { App } from "./App.jsx";
138
+ import { i18nPackageInit, i18nextPackage } from "./locale/i18n.js";
139
+
140
+ i18nPackageInit(I18nEnvironments.Browser).then(() => {
141
+ // Set the page title.
142
+ document.title = i18nextPackage.t("App.title");
143
+
144
+ createRoot(document.getElementById("root")!).render(
145
+ <StrictMode>
146
+ <App />
147
+ </StrictMode>
148
+ );
149
+ }).catch((e: unknown) => {
150
+ console.error(e);
151
+ alert(e);
152
+ });
153
+ ```
135
154
 
136
- /**
137
- * I18n wrapper properties.
138
- */
139
- export interface I18nProperties {
140
- /**
141
- * Children.
142
- */
143
- readonly children?: ReactNode | undefined;
144
- }
155
+ ## Resources
145
156
 
146
- /**
147
- * I18n wrapper. Ensures initialization of internationalization regardless of entry point.
148
- *
149
- * @param properties
150
- * Properties.
151
- *
152
- * @returns
153
- * React element.
154
- */
155
- export function I18n(properties: I18nProperties): ReactElement {
156
- const [isI18nInitialized, setIsI18nInitialized] = useState<boolean>(i18nextPackage.isInitialized);
157
-
158
- useEffect(() => {
159
- if (!isI18nInitialized) {
160
- i18nPackageInit(I18nEnvironment.Browser).then(() => {
161
- // Force refresh.
162
- setIsI18nInitialized(true);
163
- }).catch((e: unknown) => {
164
- console.error(e);
165
- alert(e);
166
- });
167
- }
168
- }, [isI18nInitialized]);
169
-
170
- return <>{isI18nInitialized ? properties.children : undefined}</>;
171
- }
172
- ```
157
+ The `resource` folder contains common resources (e.g., AIDC Toolkit icon) usable by all AIDC Toolkit packages.
173
158
 
174
- The component would then wrap the application as follows:
159
+ ## Application Data Management
175
160
 
176
- ```typescript jsx
177
- import { type ReactElement, StrictMode } from "react";
178
- import { App } from "./App";
179
- import { I18n } from "./I18n";
161
+ Parts of the AIDC Toolkit require persistent application data management, but there is no universal way to manage a persistence store:
180
162
 
181
- /**
182
- * Index.
183
- *
184
- * @returns
185
- * React element.
186
- */
187
- export default function Index(): ReactElement {
188
- return <StrictMode>
189
- <I18n>
190
- <App />
191
- </I18n>
192
- </StrictMode>;
193
- }
194
- ```
163
+ - Command-line applications use the local file system, which supports data in any format.
164
+ - Browser-based applications use local storage, which supports string data only.
165
+ - Server-based applications use a database, which may have a strict schema.
166
+ - Other applications may use a different mechanism entirely, such as storing content in an XML node in a document.
195
167
 
196
- ## Resources
168
+ The application data management functionality in this package provides a simple and consistent mechanism for managing application data. While not suitable for high-volumne or transactional data, it's sufficient for most applications that require simple key-value storage.
197
169
 
198
- The `resource` folder contains common resources (e.g., AIDC Toolkit icon) usable by all AIDC Toolkit packages.
170
+ The [`AppData`](https://aidc-toolkit.com/api/Core/type-aliases/AppData.html) type alias is a simple constrained type of `string`, `number`, `boolean`, or `object`. An object type must be one of:
171
+
172
+ - `Date`;
173
+ - `Uint8Array`;
174
+ - array of `AppData` or `undefined`; or
175
+ - object containing only `AppData` or `undefined`
176
+
177
+ Limitations in TypeScript prevent the use of recursive types and string index signatures, so there is no type enforcement beyond the basic type checks.
178
+
179
+ Two functions `encodeAppData()` and `decodeAppData()` are provided to convert between `AppData` and JSON. These enhance the normal JSON serialization/deserialization rules by:
180
+
181
+ - encoding `Date` objects as ISO 8601 strings preceded by the string "dateTime:"; and
182
+ - encoding `Uint8Array` objects as [Base64](https://developer.mozilla.org/en-US/docs/Glossary/Base64) strings preceded by the string "binary:".
183
+
184
+ The functions are generally not called directly. Rather, they are called automatically by the [`ReadOnlyAppDataStorage`](https://aidc-toolkit.com/api/Core/classes/ReadOnlyAppDataStorage.html) and [`AppDataStorage`](https://aidc-toolkit.com/api/Core/classes/AppDataStorage.html) classes, which are used to manage application data in the AIDC Toolkit packages.
185
+
186
+ Internally, each storage provider defines whether it supports binary data natively. If so, a request to read binary data will return raw content as `Uint8Array` from the underlying storage mechanism and a request to write binary data will write the `Uint8Array` as-is. This applies only to top-level read and write operations, not to `Uint8Array` values stored in nested objects.
187
+
188
+ All storage providers take a path argument, which defines an implementation-specific location for the data. The following storage providers are provided by default:
189
+
190
+ - [`LocalAppDataStorage`](https://aidc-toolkit.com/api/Core/variables/LocalAppDataStorage.html)
191
+ - Not a class, but rather a `Promise` of a constructor to one of the following implementations:
192
+ - File-based storage
193
+ - Supports binary data.
194
+ - Maps the path to an absolute or relative directory.
195
+ - Browser-based storage
196
+ - Does not support binary data.
197
+ - Maps the path by prepending it plus `/` to the key for use in [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).
198
+ - [`RemoteAppDataStorage`](https://aidc-toolkit.com/api/Core/classes/RemoteAppDataStorage.html)
199
+ - Read-only.
200
+ - Supports binary data.
201
+ - Maps the path to a base URL.
202
+
203
+ ## Caching
204
+
205
+ The [`Cache`](https://aidc-toolkit.com/api/Core/classes/Cache.html) class provides a simple cache that can be used to maintain synchronization with an external source.
@@ -0,0 +1,118 @@
1
+ import { type AppData } from "./app-data.js";
2
+ import type { Promisable } from "./type.js";
3
+ /**
4
+ * Generic read-only application data storage.
5
+ */
6
+ export declare abstract class ReadOnlyAppDataStorage<SupportsBinary extends boolean> {
7
+ #private;
8
+ /**
9
+ * Extension to identify binary data.
10
+ */
11
+ protected static readonly BINARY_EXTENSION = ".bin";
12
+ /**
13
+ * Extension to identify JSON data.
14
+ */
15
+ protected static readonly JSON_EXTENSION = ".json";
16
+ /**
17
+ * Constructor.
18
+ *
19
+ * @param supportsBinary
20
+ * True if binary data is supported.
21
+ *
22
+ * @param path
23
+ * Storage path prepended to each key along with '/' if defined, empty string if not.
24
+ */
25
+ protected constructor(supportsBinary: SupportsBinary, path?: string);
26
+ /**
27
+ * Determine if binary data is supported.
28
+ */
29
+ get supportsBinary(): SupportsBinary;
30
+ /**
31
+ * Get the storage path, prepended to each key.
32
+ */
33
+ get path(): string;
34
+ /**
35
+ * Build the full storage key.
36
+ *
37
+ * @param pathKey
38
+ * Key relative to path.
39
+ *
40
+ * @param isBinary
41
+ * True if key is to binary data, false or undefined if to string data. Ignored if binary data is not supported.
42
+ *
43
+ * @returns
44
+ * Full storage key.
45
+ */
46
+ protected fullKey(pathKey: string, isBinary: boolean): string;
47
+ /**
48
+ * Read a string or binary data from persistent storage.
49
+ *
50
+ * @param key
51
+ * Storage key (file path in Node.js, key in localStorage).
52
+ *
53
+ * @param asBinary
54
+ * True if binary data is requested, false or undefined if string data is requested. Ignored if binary data is not
55
+ * supported.
56
+ *
57
+ * @returns
58
+ * String or binary data or undefined if not found.
59
+ */
60
+ protected abstract doRead(key: string, asBinary: boolean | undefined): Promisable<(SupportsBinary extends true ? string | Uint8Array : string) | undefined>;
61
+ /**
62
+ * Read application data from storage.
63
+ *
64
+ * @param pathKey
65
+ * Key relative to path.
66
+ *
67
+ * @param asBinary
68
+ * True if binary data is requested, false or undefined if string data is requested. Ignored if binary data is not
69
+ * supported.
70
+ *
71
+ * @returns
72
+ * Application data or undefined if not found.
73
+ */
74
+ read(pathKey: string, asBinary?: boolean): Promise<AppData | undefined>;
75
+ }
76
+ /**
77
+ * Generic read/write application data storage.
78
+ */
79
+ export declare abstract class AppDataStorage<SupportsBinary extends boolean> extends ReadOnlyAppDataStorage<SupportsBinary> {
80
+ /**
81
+ * Write a string or binary data in persistent storage.
82
+ *
83
+ * @param key
84
+ * Storage key (file path in Node.js, key in localStorage).
85
+ *
86
+ * @param data
87
+ * String or binary data.
88
+ */
89
+ protected abstract doWrite(key: string, data: SupportsBinary extends true ? string | Uint8Array : string): Promisable<void>;
90
+ /**
91
+ * Write application data to storage.
92
+ *
93
+ * @param pathKey
94
+ * Key relative to path.
95
+ *
96
+ * @param appData
97
+ * Application data to write.
98
+ */
99
+ write(pathKey: string, appData: AppData): Promise<void>;
100
+ /**
101
+ * Delete from persistent storage.
102
+ *
103
+ * @param key
104
+ * Storage key (file path in Node.js, key in localStorage).
105
+ */
106
+ protected abstract doDelete(key: string): Promisable<void>;
107
+ /**
108
+ * Delete application data from persistent storage.
109
+ *
110
+ * @param pathKey
111
+ * Key relative to path.
112
+ *
113
+ * @param asBinary
114
+ * True if key is to binary data, false or undefined if to string data. Ignored if binary data is not supported.
115
+ */
116
+ delete(pathKey: string, asBinary?: boolean): Promise<void>;
117
+ }
118
+ //# sourceMappingURL=app-data-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-data-storage.d.ts","sourceRoot":"","sources":["../src/app-data-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAgC,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C;;GAEG;AACH,8BAAsB,sBAAsB,CAAC,cAAc,SAAS,OAAO;;IACvE;;OAEG;IACH,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,UAAU;IAEpD;;OAEG;IACH,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,WAAW;IAYnD;;;;;;;;OAQG;IACH,SAAS,aAAa,cAAc,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,MAAM;IAKnE;;OAEG;IACH,IAAI,cAAc,IAAI,cAAc,CAEnC;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM;IAS7D;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC,cAAc,SAAS,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAE3J;;;;;;;;;;;;OAYG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;CAKhF;AAED;;GAEG;AACH,8BAAsB,cAAc,CAAC,cAAc,SAAS,OAAO,CAAE,SAAQ,sBAAsB,CAAC,cAAc,CAAC;IAC/G;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,SAAS,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC;IAE3H;;;;;;;;OAQG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAU7D;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC;IAE1D;;;;;;;;OAQG;IACG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAGnE"}
@@ -0,0 +1,117 @@
1
+ import { decodeAppData, encodeAppData } from "./app-data.js";
2
+ /**
3
+ * Generic read-only application data storage.
4
+ */
5
+ export class ReadOnlyAppDataStorage {
6
+ /**
7
+ * Extension to identify binary data.
8
+ */
9
+ static BINARY_EXTENSION = ".bin";
10
+ /**
11
+ * Extension to identify JSON data.
12
+ */
13
+ static JSON_EXTENSION = ".json";
14
+ /**
15
+ * True if binary data is supported natively.
16
+ */
17
+ #supportsBinary;
18
+ /**
19
+ * Storage path prepended to each key.
20
+ */
21
+ #path;
22
+ /**
23
+ * Constructor.
24
+ *
25
+ * @param supportsBinary
26
+ * True if binary data is supported.
27
+ *
28
+ * @param path
29
+ * Storage path prepended to each key along with '/' if defined, empty string if not.
30
+ */
31
+ constructor(supportsBinary, path) {
32
+ this.#supportsBinary = supportsBinary;
33
+ this.#path = path !== undefined ? `${path}/` : "";
34
+ }
35
+ /**
36
+ * Determine if binary data is supported.
37
+ */
38
+ get supportsBinary() {
39
+ return this.#supportsBinary;
40
+ }
41
+ /**
42
+ * Get the storage path, prepended to each key.
43
+ */
44
+ get path() {
45
+ return this.#path;
46
+ }
47
+ /**
48
+ * Build the full storage key.
49
+ *
50
+ * @param pathKey
51
+ * Key relative to path.
52
+ *
53
+ * @param isBinary
54
+ * True if key is to binary data, false or undefined if to string data. Ignored if binary data is not supported.
55
+ *
56
+ * @returns
57
+ * Full storage key.
58
+ */
59
+ fullKey(pathKey, isBinary) {
60
+ const keyNoExtension = `${this.path}${pathKey}`;
61
+ // Add extension to key if binary data is supported.
62
+ return this.supportsBinary ?
63
+ `${keyNoExtension}${isBinary ? ReadOnlyAppDataStorage.BINARY_EXTENSION : ReadOnlyAppDataStorage.JSON_EXTENSION}` :
64
+ keyNoExtension;
65
+ }
66
+ /**
67
+ * Read application data from storage.
68
+ *
69
+ * @param pathKey
70
+ * Key relative to path.
71
+ *
72
+ * @param asBinary
73
+ * True if binary data is requested, false or undefined if string data is requested. Ignored if binary data is not
74
+ * supported.
75
+ *
76
+ * @returns
77
+ * Application data or undefined if not found.
78
+ */
79
+ async read(pathKey, asBinary) {
80
+ const data = await this.doRead(this.fullKey(pathKey, asBinary === true), asBinary);
81
+ return typeof data === "string" ? decodeAppData(data) : data;
82
+ }
83
+ }
84
+ /**
85
+ * Generic read/write application data storage.
86
+ */
87
+ export class AppDataStorage extends ReadOnlyAppDataStorage {
88
+ /**
89
+ * Write application data to storage.
90
+ *
91
+ * @param pathKey
92
+ * Key relative to path.
93
+ *
94
+ * @param appData
95
+ * Application data to write.
96
+ */
97
+ async write(pathKey, appData) {
98
+ const isBinary = appData instanceof Uint8Array;
99
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Type is determined by supports binary flag.
100
+ await this.doWrite(this.fullKey(pathKey, isBinary), (this.supportsBinary && isBinary ?
101
+ appData :
102
+ encodeAppData(appData)));
103
+ }
104
+ /**
105
+ * Delete application data from persistent storage.
106
+ *
107
+ * @param pathKey
108
+ * Key relative to path.
109
+ *
110
+ * @param asBinary
111
+ * True if key is to binary data, false or undefined if to string data. Ignored if binary data is not supported.
112
+ */
113
+ async delete(pathKey, asBinary) {
114
+ await this.doDelete(this.fullKey(pathKey, asBinary === true));
115
+ }
116
+ }
117
+ //# sourceMappingURL=app-data-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-data-storage.js","sourceRoot":"","sources":["../src/app-data-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG3E;;GAEG;AACH,MAAM,OAAgB,sBAAsB;IACxC;;OAEG;IACO,MAAM,CAAU,gBAAgB,GAAG,MAAM,CAAC;IAEpD;;OAEG;IACO,MAAM,CAAU,cAAc,GAAG,OAAO,CAAC;IAEnD;;OAEG;IACM,eAAe,CAAiB;IAEzC;;OAEG;IACM,KAAK,CAAS;IAEvB;;;;;;;;OAQG;IACH,YAAsB,cAA8B,EAAE,IAAa;QAC/D,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC;QACtC,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,IAAI,cAAc;QACd,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;;;;;;;;;;OAWG;IACO,OAAO,CAAC,OAAe,EAAE,QAAiB;QAChD,MAAM,cAAc,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,CAAC;QAEhD,oDAAoD;QACpD,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC;YACxB,GAAG,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,gBAAgB,CAAC,CAAC,CAAC,sBAAsB,CAAC,cAAc,EAAE,CAAC,CAAC;YAClH,cAAc,CAAC;IACvB,CAAC;IAiBD;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,QAAkB;QAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;QAEnF,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,CAAC;;AAGL;;GAEG;AACH,MAAM,OAAgB,cAA+C,SAAQ,sBAAsC;IAY/G;;;;;;;;OAQG;IACH,KAAK,CAAC,KAAK,CAAC,OAAe,EAAE,OAAgB;QACzC,MAAM,QAAQ,GAAG,OAAO,YAAY,UAAU,CAAC;QAE/C,sHAAsH;QACtH,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,IAAI,QAAQ,CAAC,CAAC;YAClF,OAAO,CAAC,CAAC;YACT,aAAa,CAAC,OAAO,CAAC,CACa,CAAC,CAAC;IAC7C,CAAC;IAUD;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,QAAkB;QAC5C,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC;IAClE,CAAC;CACJ"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Application data.
3
+ */
4
+ export type AppData = string | number | boolean | object;
5
+ /**
6
+ * Decode application data from an encoded string.
7
+ *
8
+ * @param stringData
9
+ * String data.
10
+ *
11
+ * @returns
12
+ * Decoded application data.
13
+ */
14
+ export declare function decodeAppData(stringData: string): AppData | undefined;
15
+ /**
16
+ * Encode application data as a string for storage. Encoded string is in JSON format with date/time and binary data
17
+ * converted to identifiable strings for decoding.
18
+ *
19
+ * @param appData
20
+ * Application data.
21
+ *
22
+ * @returns
23
+ * Encoded application data.
24
+ */
25
+ export declare function encodeAppData(appData: AppData): string;
26
+ //# sourceMappingURL=app-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-data.d.ts","sourceRoot":"","sources":["../src/app-data.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAqCrE;AA4BD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,79 @@
1
+ import { fromByteArray, toByteArray } from "base64-js";
2
+ /**
3
+ * Decode application data from an encoded string.
4
+ *
5
+ * @param stringData
6
+ * String data.
7
+ *
8
+ * @returns
9
+ * Decoded application data.
10
+ */
11
+ export function decodeAppData(stringData) {
12
+ let decodedAppData;
13
+ try {
14
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Mapping is expected to be correct.
15
+ decodedAppData = JSON.parse(stringData, (_key, value) => {
16
+ let replacementValue = value;
17
+ // Decode string representing date/time and binary array and pass through other values unmodified.
18
+ if (typeof value === "string") {
19
+ // First capture group is type, second is data; simple split at ':' character.
20
+ const stringDataGroups = /^(?<type>\w+):(?<data>.*)$/u.exec(value)?.groups;
21
+ if (stringDataGroups !== undefined) {
22
+ const type = stringDataGroups["type"];
23
+ const data = stringDataGroups["data"];
24
+ switch (type) {
25
+ case "dateTime":
26
+ replacementValue = new Date(data);
27
+ break;
28
+ case "binary":
29
+ replacementValue = toByteArray(data);
30
+ break;
31
+ }
32
+ }
33
+ }
34
+ return replacementValue;
35
+ });
36
+ }
37
+ catch {
38
+ // String data is not valid JSON; discard it.
39
+ decodedAppData = undefined;
40
+ }
41
+ return decodedAppData;
42
+ }
43
+ /**
44
+ * Encode an object to a format suitable for storage.
45
+ *
46
+ * @param o
47
+ * Object.
48
+ *
49
+ * @returns
50
+ * Object suitable for storage with date/time and binary types encoded as strings.
51
+ */
52
+ function encodeObject(o) {
53
+ let mappedData;
54
+ // Encode date/time and binary array as string and pass through other values unmodified.
55
+ if (o instanceof Date) {
56
+ mappedData = `dateTime:${o.toISOString()}`;
57
+ }
58
+ else if (o instanceof Uint8Array) {
59
+ mappedData = `binary:${fromByteArray(o)}`;
60
+ }
61
+ else {
62
+ mappedData = Object.fromEntries(Object.entries(o).map(([key, value]) => [key, typeof value === "object" && value !== null ? encodeObject(value) : value]));
63
+ }
64
+ return mappedData;
65
+ }
66
+ /**
67
+ * Encode application data as a string for storage. Encoded string is in JSON format with date/time and binary data
68
+ * converted to identifiable strings for decoding.
69
+ *
70
+ * @param appData
71
+ * Application data.
72
+ *
73
+ * @returns
74
+ * Encoded application data.
75
+ */
76
+ export function encodeAppData(appData) {
77
+ return JSON.stringify(typeof appData !== "object" ? appData : encodeObject(appData));
78
+ }
79
+ //# sourceMappingURL=app-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-data.js","sourceRoot":"","sources":["../src/app-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAOvD;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC5C,IAAI,cAAmC,CAAC;IAExC,IAAI,CAAC;QACD,6GAA6G;QAC7G,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAc,EAAE,EAAE;YAC7D,IAAI,gBAAgB,GAAG,KAAK,CAAC;YAE7B,kGAAkG;YAClG,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC5B,8EAA8E;gBAC9E,MAAM,gBAAgB,GAAG,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;gBAE3E,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;oBACjC,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;oBACtC,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;oBAEtC,QAAQ,IAAI,EAAE,CAAC;wBACX,KAAK,UAAU;4BACX,gBAAgB,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;4BAClC,MAAM;wBAEV,KAAK,QAAQ;4BACT,gBAAgB,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;4BACrC,MAAM;oBACd,CAAC;gBACL,CAAC;YACL,CAAC;YAED,OAAO,gBAAgB,CAAC;QAC5B,CAAC,CAAY,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACL,6CAA6C;QAC7C,cAAc,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED,OAAO,cAAc,CAAC;AAC1B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,YAAY,CAAC,CAAS;IAC3B,IAAI,UAA2B,CAAC;IAEhC,wFAAwF;IACxF,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;QACpB,UAAU,GAAG,YAAY,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;IAC/C,CAAC;SAAM,IAAI,CAAC,YAAY,UAAU,EAAE,CAAC;QACjC,UAAU,GAAG,UAAU,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,CAAC;SAAM,CAAC;QACJ,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAoB,EAAE,EAAE,CACtF,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CACnF,CAAC,CAAC;IACP,CAAC;IAED,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { AppDataStorage } from "./app-data-storage.js";
2
+ /**
3
+ * Application data storage using the browser local storage.
4
+ */
5
+ export declare class BrowserAppDataStorage extends AppDataStorage<false> {
6
+ /**
7
+ * Constructor.
8
+ *
9
+ * @param path
10
+ * Storage path prepended to each key along with '/' if defined, empty string if not.
11
+ */
12
+ constructor(path?: string);
13
+ /**
14
+ * @inheritDoc
15
+ */
16
+ protected doRead(key: string): string | undefined;
17
+ /**
18
+ * @inheritDoc
19
+ */
20
+ protected doWrite(key: string, s: string): void;
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ protected doDelete(key: string): void;
25
+ }
26
+ //# sourceMappingURL=browser-app-data-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-app-data-storage.d.ts","sourceRoot":"","sources":["../src/browser-app-data-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,cAAc,CAAC,KAAK,CAAC;IAC5D;;;;;OAKG;gBACS,IAAI,CAAC,EAAE,MAAM;IAIzB;;OAEG;cACgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI1D;;OAEG;cACgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAIxD;;OAEG;cACgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAGjD"}