@aidc-toolkit/core 1.0.32-beta → 1.0.33-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.
- package/README.md +98 -87
- package/dist/app-data-storage.d.ts +118 -0
- package/dist/app-data-storage.d.ts.map +1 -0
- package/dist/app-data-storage.js +117 -0
- package/dist/app-data-storage.js.map +1 -0
- package/dist/app-data.d.ts +26 -0
- package/dist/app-data.d.ts.map +1 -0
- package/dist/app-data.js +79 -0
- package/dist/app-data.js.map +1 -0
- package/dist/browser-app-data-storage.d.ts +26 -0
- package/dist/browser-app-data-storage.d.ts.map +1 -0
- package/dist/browser-app-data-storage.js +34 -0
- package/dist/browser-app-data-storage.js.map +1 -0
- package/dist/file-app-data-storage.d.ts +26 -0
- package/dist/file-app-data-storage.d.ts.map +1 -0
- package/dist/file-app-data-storage.js +40 -0
- package/dist/file-app-data-storage.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/local-app-data-storage.d.ts +8 -0
- package/dist/local-app-data-storage.d.ts.map +1 -0
- package/dist/local-app-data-storage.js +11 -0
- package/dist/local-app-data-storage.js.map +1 -0
- package/dist/locale/en/locale-resources.d.ts +3 -0
- package/dist/locale/en/locale-resources.d.ts.map +1 -1
- package/dist/locale/en/locale-resources.js +3 -0
- package/dist/locale/en/locale-resources.js.map +1 -1
- package/dist/locale/fr/locale-resources.d.ts +3 -0
- package/dist/locale/fr/locale-resources.d.ts.map +1 -1
- package/dist/locale/fr/locale-resources.js +3 -0
- package/dist/locale/fr/locale-resources.js.map +1 -1
- package/dist/locale/i18n.d.ts +22 -13
- package/dist/locale/i18n.d.ts.map +1 -1
- package/dist/locale/i18n.js +62 -43
- package/dist/locale/i18n.js.map +1 -1
- package/dist/logger.d.ts +4 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +9 -3
- package/dist/logger.js.map +1 -1
- package/dist/remote-app-data-storage.d.ts +18 -0
- package/dist/remote-app-data-storage.d.ts.map +1 -0
- package/dist/remote-app-data-storage.js +37 -0
- package/dist/remote-app-data-storage.js.map +1 -0
- package/dist/type-helper.js.map +1 -1
- package/package.json +9 -7
- package/src/app-data-storage.ts +166 -0
- package/src/app-data.ts +94 -0
- package/src/browser-app-data-storage.ts +37 -0
- package/src/file-app-data-storage.ts +49 -0
- package/src/index.ts +5 -0
- package/src/local-app-data-storage.ts +12 -0
- package/src/locale/en/locale-resources.ts +3 -0
- package/src/locale/fr/locale-resources.ts +3 -0
- package/src/locale/i18n.ts +81 -49
- package/src/logger.ts +10 -3
- package/src/remote-app-data-storage.ts +38 -0
- package/src/type-helper.ts +2 -2
package/dist/type-helper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-helper.js","sourceRoot":"","sources":["../src/type-helper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAS,UAAU,CAAgE,QAAkB,EAAE,CAAI,EAAE,GAAG,IAAS;IACrH,yGAAyG;IACzG,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAQ,CAAC,KAAK,QAAQ,CAAC,CAAkD,CAAC;AAC1J,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,IAAI,CAAsC,CAAI,EAAE,GAAG,IAAS;IACxE,OAAO,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,IAAI,CAAsC,CAAI,EAAE,GAAG,IAAS;IACxE,OAAO,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,OAAO,CAAyE,IAAW,EAAE,MAAe;IACxH,0FAA0F;IAC1F,OAAO,IAAI,CAAC,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"type-helper.js","sourceRoot":"","sources":["../src/type-helper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAS,UAAU,CAAgE,QAAkB,EAAE,CAAI,EAAE,GAAG,IAAS;IACrH,yGAAyG;IACzG,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAQ,CAAC,KAAK,QAAQ,CAAC,CAAkD,CAAC;AAC1J,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,IAAI,CAAsC,CAAI,EAAE,GAAG,IAAS;IACxE,OAAO,UAAU,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,IAAI,CAAsC,CAAI,EAAE,GAAG,IAAS;IACxE,OAAO,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,OAAO,CAAyE,IAAW,EAAE,MAAe;IACxH,0FAA0F;IAC1F,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAQ,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,OAAO,CAAyE,IAAW,EAAE,MAAe;IACxH,0FAA0F;IAC1F,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAQ,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,UAAU,CAA4D,CAAI,EAAE,GAAM;IAC9F,2GAA2G;IAC3G,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACd;YACI,sFAAsF;YACtF,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAY;SAC3B,CAAC,CAAC;QACH,EAAE,CAC2C,CAAC;AACtD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,QAAiB;IACvC,OAAO,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,CAAC;AACvD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aidc-toolkit/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33-beta",
|
|
4
4
|
"description": "Core functionality for AIDC Toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
|
-
"homepage": "https://aidc-toolkit.com
|
|
7
|
+
"homepage": "https://aidc-toolkit.com",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/aidc-toolkit/core.git"
|
|
@@ -20,15 +20,17 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"lint": "eslint",
|
|
23
|
-
"tsc
|
|
24
|
-
"build:
|
|
25
|
-
"build:
|
|
26
|
-
"build:
|
|
23
|
+
"tsc-src": "tsc --project tsconfig-src.json",
|
|
24
|
+
"build:alpha": "rimraf dist && npm run tsc-src -- --declarationMap --sourceMap",
|
|
25
|
+
"build:beta": "npm run build:alpha",
|
|
26
|
+
"build:prod": "npm run tsc-src -- --noEmit && tsup",
|
|
27
|
+
"build:doc": "npm run build:alpha"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@aidc-toolkit/dev": "1.0.
|
|
30
|
+
"@aidc-toolkit/dev": "1.0.33-beta"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
33
|
+
"base64-js": "^1.5.1",
|
|
32
34
|
"i18next": "^25.7.4",
|
|
33
35
|
"i18next-browser-languagedetector": "^8.2.0",
|
|
34
36
|
"i18next-cli-language-detector": "^1.1.8",
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { type AppData, decodeAppData, encodeAppData } from "./app-data.js";
|
|
2
|
+
import type { Promisable } from "./type.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generic read-only application data storage.
|
|
6
|
+
*/
|
|
7
|
+
export abstract class ReadOnlyAppDataStorage<SupportsBinary extends boolean> {
|
|
8
|
+
/**
|
|
9
|
+
* Extension to identify binary data.
|
|
10
|
+
*/
|
|
11
|
+
protected static readonly BINARY_EXTENSION = ".bin";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extension to identify JSON data.
|
|
15
|
+
*/
|
|
16
|
+
protected static readonly JSON_EXTENSION = ".json";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* True if binary data is supported natively.
|
|
20
|
+
*/
|
|
21
|
+
readonly #supportsBinary: SupportsBinary;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Storage path prepended to each key.
|
|
25
|
+
*/
|
|
26
|
+
readonly #path: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Constructor.
|
|
30
|
+
*
|
|
31
|
+
* @param supportsBinary
|
|
32
|
+
* True if binary data is supported.
|
|
33
|
+
*
|
|
34
|
+
* @param path
|
|
35
|
+
* Storage path prepended to each key along with '/' if defined, empty string if not.
|
|
36
|
+
*/
|
|
37
|
+
protected constructor(supportsBinary: SupportsBinary, path?: string) {
|
|
38
|
+
this.#supportsBinary = supportsBinary;
|
|
39
|
+
this.#path = path !== undefined ? `${path}/` : "";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Determine if binary data is supported.
|
|
44
|
+
*/
|
|
45
|
+
get supportsBinary(): SupportsBinary {
|
|
46
|
+
return this.#supportsBinary;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the storage path, prepended to each key.
|
|
51
|
+
*/
|
|
52
|
+
get path(): string {
|
|
53
|
+
return this.#path;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build the full storage key.
|
|
58
|
+
*
|
|
59
|
+
* @param pathKey
|
|
60
|
+
* Key relative to path.
|
|
61
|
+
*
|
|
62
|
+
* @param isBinary
|
|
63
|
+
* True if key is to binary data, false or undefined if to string data. Ignored if binary data is not supported.
|
|
64
|
+
*
|
|
65
|
+
* @returns
|
|
66
|
+
* Full storage key.
|
|
67
|
+
*/
|
|
68
|
+
protected fullKey(pathKey: string, isBinary: boolean): string {
|
|
69
|
+
const keyNoExtension = `${this.path}${pathKey}`;
|
|
70
|
+
|
|
71
|
+
// Add extension to key if binary data is supported.
|
|
72
|
+
return this.supportsBinary ?
|
|
73
|
+
`${keyNoExtension}${isBinary ? ReadOnlyAppDataStorage.BINARY_EXTENSION : ReadOnlyAppDataStorage.JSON_EXTENSION}` :
|
|
74
|
+
keyNoExtension;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Read a string or binary data from persistent storage.
|
|
79
|
+
*
|
|
80
|
+
* @param key
|
|
81
|
+
* Storage key (file path in Node.js, key in localStorage).
|
|
82
|
+
*
|
|
83
|
+
* @param asBinary
|
|
84
|
+
* True if binary data is requested, false or undefined if string data is requested. Ignored if binary data is not
|
|
85
|
+
* supported.
|
|
86
|
+
*
|
|
87
|
+
* @returns
|
|
88
|
+
* String or binary data or undefined if not found.
|
|
89
|
+
*/
|
|
90
|
+
protected abstract doRead(key: string, asBinary: boolean | undefined): Promisable<(SupportsBinary extends true ? string | Uint8Array : string) | undefined>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Read application data from storage.
|
|
94
|
+
*
|
|
95
|
+
* @param pathKey
|
|
96
|
+
* Key relative to path.
|
|
97
|
+
*
|
|
98
|
+
* @param asBinary
|
|
99
|
+
* True if binary data is requested, false or undefined if string data is requested. Ignored if binary data is not
|
|
100
|
+
* supported.
|
|
101
|
+
*
|
|
102
|
+
* @returns
|
|
103
|
+
* Application data or undefined if not found.
|
|
104
|
+
*/
|
|
105
|
+
async read(pathKey: string, asBinary?: boolean): Promise<AppData | undefined> {
|
|
106
|
+
const data = await this.doRead(this.fullKey(pathKey, asBinary === true), asBinary);
|
|
107
|
+
|
|
108
|
+
return typeof data === "string" ? decodeAppData(data) : data;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generic read/write application data storage.
|
|
114
|
+
*/
|
|
115
|
+
export abstract class AppDataStorage<SupportsBinary extends boolean> extends ReadOnlyAppDataStorage<SupportsBinary> {
|
|
116
|
+
/**
|
|
117
|
+
* Write a string or binary data in persistent storage.
|
|
118
|
+
*
|
|
119
|
+
* @param key
|
|
120
|
+
* Storage key (file path in Node.js, key in localStorage).
|
|
121
|
+
*
|
|
122
|
+
* @param data
|
|
123
|
+
* String or binary data.
|
|
124
|
+
*/
|
|
125
|
+
protected abstract doWrite(key: string, data: SupportsBinary extends true ? string | Uint8Array : string): Promisable<void>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Write application data to storage.
|
|
129
|
+
*
|
|
130
|
+
* @param pathKey
|
|
131
|
+
* Key relative to path.
|
|
132
|
+
*
|
|
133
|
+
* @param appData
|
|
134
|
+
* Application data to write.
|
|
135
|
+
*/
|
|
136
|
+
async write(pathKey: string, appData: AppData): Promise<void> {
|
|
137
|
+
const isBinary = appData instanceof Uint8Array;
|
|
138
|
+
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Type is determined by supports binary flag.
|
|
140
|
+
await this.doWrite(this.fullKey(pathKey, isBinary), (this.supportsBinary && isBinary ?
|
|
141
|
+
appData :
|
|
142
|
+
encodeAppData(appData)
|
|
143
|
+
) as Parameters<typeof this.doWrite>[1]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Delete from persistent storage.
|
|
148
|
+
*
|
|
149
|
+
* @param key
|
|
150
|
+
* Storage key (file path in Node.js, key in localStorage).
|
|
151
|
+
*/
|
|
152
|
+
protected abstract doDelete(key: string): Promisable<void>;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Delete application data from persistent storage.
|
|
156
|
+
*
|
|
157
|
+
* @param pathKey
|
|
158
|
+
* Key relative to path.
|
|
159
|
+
*
|
|
160
|
+
* @param asBinary
|
|
161
|
+
* True if key is to binary data, false or undefined if to string data. Ignored if binary data is not supported.
|
|
162
|
+
*/
|
|
163
|
+
async delete(pathKey: string, asBinary?: boolean): Promise<void> {
|
|
164
|
+
await this.doDelete(this.fullKey(pathKey, asBinary === true));
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/app-data.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { fromByteArray, toByteArray } from "base64-js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Application data.
|
|
5
|
+
*/
|
|
6
|
+
export type AppData = string | number | boolean | object;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Decode application data from an encoded string.
|
|
10
|
+
*
|
|
11
|
+
* @param stringData
|
|
12
|
+
* String data.
|
|
13
|
+
*
|
|
14
|
+
* @returns
|
|
15
|
+
* Decoded application data.
|
|
16
|
+
*/
|
|
17
|
+
export function decodeAppData(stringData: string): AppData | undefined {
|
|
18
|
+
let decodedAppData: AppData | undefined;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Mapping is expected to be correct.
|
|
22
|
+
decodedAppData = JSON.parse(stringData, (_key, value: unknown) => {
|
|
23
|
+
let replacementValue = value;
|
|
24
|
+
|
|
25
|
+
// Decode string representing date/time and binary array and pass through other values unmodified.
|
|
26
|
+
if (typeof value === "string") {
|
|
27
|
+
// First capture group is type, second is data; simple split at ':' character.
|
|
28
|
+
const stringDataGroups = /^(?<type>\w+):(?<data>.*)$/u.exec(value)?.groups;
|
|
29
|
+
|
|
30
|
+
if (stringDataGroups !== undefined) {
|
|
31
|
+
const type = stringDataGroups["type"];
|
|
32
|
+
const data = stringDataGroups["data"];
|
|
33
|
+
|
|
34
|
+
switch (type) {
|
|
35
|
+
case "dateTime":
|
|
36
|
+
replacementValue = new Date(data);
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case "binary":
|
|
40
|
+
replacementValue = toByteArray(data);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return replacementValue;
|
|
47
|
+
}) as AppData;
|
|
48
|
+
} catch {
|
|
49
|
+
// String data is not valid JSON; discard it.
|
|
50
|
+
decodedAppData = undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return decodedAppData;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Encode an object to a format suitable for storage.
|
|
58
|
+
*
|
|
59
|
+
* @param o
|
|
60
|
+
* Object.
|
|
61
|
+
*
|
|
62
|
+
* @returns
|
|
63
|
+
* Object suitable for storage with date/time and binary types encoded as strings.
|
|
64
|
+
*/
|
|
65
|
+
function encodeObject(o: object): object | string {
|
|
66
|
+
let mappedData: object | string;
|
|
67
|
+
|
|
68
|
+
// Encode date/time and binary array as string and pass through other values unmodified.
|
|
69
|
+
if (o instanceof Date) {
|
|
70
|
+
mappedData = `dateTime:${o.toISOString()}`;
|
|
71
|
+
} else if (o instanceof Uint8Array) {
|
|
72
|
+
mappedData = `binary:${fromByteArray(o)}`;
|
|
73
|
+
} else {
|
|
74
|
+
mappedData = Object.fromEntries(Object.entries(o).map(([key, value]: [string, unknown]) =>
|
|
75
|
+
[key, typeof value === "object" && value !== null ? encodeObject(value) : value]
|
|
76
|
+
));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return mappedData;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Encode application data as a string for storage. Encoded string is in JSON format with date/time and binary data
|
|
84
|
+
* converted to identifiable strings for decoding.
|
|
85
|
+
*
|
|
86
|
+
* @param appData
|
|
87
|
+
* Application data.
|
|
88
|
+
*
|
|
89
|
+
* @returns
|
|
90
|
+
* Encoded application data.
|
|
91
|
+
*/
|
|
92
|
+
export function encodeAppData(appData: AppData): string {
|
|
93
|
+
return JSON.stringify(typeof appData !== "object" ? appData : encodeObject(appData));
|
|
94
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AppDataStorage } from "./app-data-storage.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Application data storage using the browser local storage.
|
|
5
|
+
*/
|
|
6
|
+
export class BrowserAppDataStorage extends AppDataStorage<false> {
|
|
7
|
+
/**
|
|
8
|
+
* Constructor.
|
|
9
|
+
*
|
|
10
|
+
* @param path
|
|
11
|
+
* Storage path prepended to each key along with '/' if defined, empty string if not.
|
|
12
|
+
*/
|
|
13
|
+
constructor(path?: string) {
|
|
14
|
+
super(false, path);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @inheritDoc
|
|
19
|
+
*/
|
|
20
|
+
protected override doRead(key: string): string | undefined {
|
|
21
|
+
return localStorage.getItem(key) ?? undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @inheritDoc
|
|
26
|
+
*/
|
|
27
|
+
protected override doWrite(key: string, s: string): void {
|
|
28
|
+
localStorage.setItem(key, s);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @inheritDoc
|
|
33
|
+
*/
|
|
34
|
+
protected override doDelete(key: string): void {
|
|
35
|
+
localStorage.removeItem(key);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { AppDataStorage } from "./app-data-storage.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Application data storage using the file system.
|
|
7
|
+
*/
|
|
8
|
+
export class FileAppDataStorage extends AppDataStorage<true> {
|
|
9
|
+
/**
|
|
10
|
+
* Constructor.
|
|
11
|
+
*
|
|
12
|
+
* @param path
|
|
13
|
+
* Storage path prepended to each key along with '/' if defined, empty string if not.
|
|
14
|
+
*/
|
|
15
|
+
constructor(path?: string) {
|
|
16
|
+
super(true, path);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @inheritDoc
|
|
21
|
+
*/
|
|
22
|
+
protected override async doRead(key: string, asBinary: boolean | undefined): Promise<string | Uint8Array | undefined> {
|
|
23
|
+
return fs.promises.readFile(key).then(buffer =>
|
|
24
|
+
asBinary === true ? buffer : buffer.toString()
|
|
25
|
+
).catch(() =>
|
|
26
|
+
undefined
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @inheritDoc
|
|
32
|
+
*/
|
|
33
|
+
protected override async doWrite(key: string, data: string | Uint8Array): Promise<void> {
|
|
34
|
+
return fs.promises.mkdir(path.dirname(key), {
|
|
35
|
+
recursive: true
|
|
36
|
+
}).then(async () =>
|
|
37
|
+
fs.promises.writeFile(key, data)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @inheritDoc
|
|
43
|
+
*/
|
|
44
|
+
protected override async doDelete(key: string): Promise<void> {
|
|
45
|
+
return fs.promises.rm(key, {
|
|
46
|
+
force: true
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -19,7 +19,12 @@ export * from "./type-helper.js";
|
|
|
19
19
|
|
|
20
20
|
export * from "./logger.js";
|
|
21
21
|
|
|
22
|
+
export * from "./app-data.js";
|
|
23
|
+
export * from "./app-data-storage.js";
|
|
24
|
+
export * from "./local-app-data-storage.js";
|
|
25
|
+
export * from "./remote-app-data-storage.js";
|
|
22
26
|
export * from "./cache.js";
|
|
27
|
+
|
|
23
28
|
export type * from "./hyperlink.js";
|
|
24
29
|
|
|
25
30
|
export * from "./locale/i18n.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AppDataStorage } from "./app-data-storage.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Local application data storage class for the current environment. This is a variable representing a `Promise` as the
|
|
5
|
+
* implementing class is loaded dynamically to prevent the inclusion of unnecessary node dependencies in a browser
|
|
6
|
+
* environment.
|
|
7
|
+
*/
|
|
8
|
+
export const LocalAppDataStorage: Promise<new (path?: string) => AppDataStorage<boolean>> =
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- localStorage is undefined when running under Node.js.
|
|
10
|
+
globalThis.localStorage === undefined ?
|
|
11
|
+
import("./file-app-data-storage.js").then(module => module.FileAppDataStorage) :
|
|
12
|
+
import("./browser-app-data-storage.js").then(module => module.BrowserAppDataStorage);
|
package/src/locale/i18n.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import i18next, {
|
|
1
|
+
import i18next, {
|
|
2
|
+
type i18n,
|
|
3
|
+
type LanguageDetectorModule,
|
|
4
|
+
type Module,
|
|
5
|
+
type Newable,
|
|
6
|
+
type NewableModule,
|
|
7
|
+
type Resource
|
|
8
|
+
} from "i18next";
|
|
2
9
|
import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
|
|
3
10
|
import I18nextCLILanguageDetector from "i18next-cli-language-detector";
|
|
4
11
|
import enLocaleResources from "./en/locale-resources.js";
|
|
@@ -63,9 +70,9 @@ export const coreNS = "aidct_core";
|
|
|
63
70
|
export type CoreLocaleResources = typeof enLocaleResources;
|
|
64
71
|
|
|
65
72
|
/**
|
|
66
|
-
* Core
|
|
73
|
+
* Core resource bundle.
|
|
67
74
|
*/
|
|
68
|
-
export const
|
|
75
|
+
export const coreResourceBundle: Resource = {
|
|
69
76
|
en: {
|
|
70
77
|
aidct_core: enLocaleResources
|
|
71
78
|
},
|
|
@@ -77,24 +84,11 @@ export const coreResources: Resource = {
|
|
|
77
84
|
// Explicit type is necessary because type can't be inferred without additional references.
|
|
78
85
|
export const i18nextCore: i18n = i18next.createInstance();
|
|
79
86
|
|
|
80
|
-
/**
|
|
81
|
-
* Initialize internationalization.
|
|
82
|
-
*
|
|
83
|
-
* @param environment
|
|
84
|
-
* Environment in which the application is running.
|
|
85
|
-
*
|
|
86
|
-
* @param debug
|
|
87
|
-
* Debug setting.
|
|
88
|
-
*/
|
|
89
|
-
export async function i18nCoreInit(environment: I18nEnvironment, debug = false): Promise<void> {
|
|
90
|
-
await i18nFinalizeInit(i18nextCore, environment, debug, coreNS, coreResources);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
87
|
/**
|
|
94
88
|
* Initialize internationalization.
|
|
95
89
|
*
|
|
96
90
|
* @param i18next
|
|
97
|
-
* Internationalization object. As multiple objects
|
|
91
|
+
* Internationalization object. As multiple objects exist, this parameter represents the one for the module for which
|
|
98
92
|
* internationalization is being initialized.
|
|
99
93
|
*
|
|
100
94
|
* @param environment
|
|
@@ -106,56 +100,94 @@ export async function i18nCoreInit(environment: I18nEnvironment, debug = false):
|
|
|
106
100
|
* @param defaultNS
|
|
107
101
|
* Default namespace.
|
|
108
102
|
*
|
|
109
|
-
* @param
|
|
110
|
-
*
|
|
103
|
+
* @param defaultResourceBundle
|
|
104
|
+
* Default resource bundle.
|
|
105
|
+
*
|
|
106
|
+
* @param i18nDependencyInits
|
|
107
|
+
* Dependency internationalization initialization functions.
|
|
108
|
+
*
|
|
109
|
+
* @returns
|
|
110
|
+
* Default resource bundle.
|
|
111
111
|
*/
|
|
112
|
-
export async function
|
|
112
|
+
export async function i18nInit(i18next: i18n, environment: I18nEnvironment, debug: boolean, defaultNS: string, defaultResourceBundle: Resource, ...i18nDependencyInits: Array<(environment: I18nEnvironment, debug: boolean) => Promise<Resource>>): Promise<Resource> {
|
|
113
113
|
// Initialization may be called more than once.
|
|
114
114
|
if (!i18next.isInitialized) {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
const mergedResourceBundle: Resource = {};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Merge a package resource bundle into the merged resource bundle.
|
|
119
|
+
*
|
|
120
|
+
* @param resourceBundle
|
|
121
|
+
* Package resource bundle.
|
|
122
|
+
*/
|
|
123
|
+
function mergeResourceBundle(resourceBundle: Resource): void {
|
|
119
124
|
// Merge languages.
|
|
120
|
-
for (const [language,
|
|
121
|
-
if (!(language in
|
|
122
|
-
|
|
125
|
+
for (const [language, languageResourceBundle] of Object.entries(resourceBundle)) {
|
|
126
|
+
if (!(language in mergedResourceBundle)) {
|
|
127
|
+
mergedResourceBundle[language] = {};
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
const
|
|
130
|
+
const mergedLanguageResourceBundle = mergedResourceBundle[language];
|
|
126
131
|
|
|
127
132
|
// Merge namespaces.
|
|
128
|
-
for (const [namespace, resourceKey] of Object.entries(
|
|
129
|
-
|
|
133
|
+
for (const [namespace, resourceKey] of Object.entries(languageResourceBundle)) {
|
|
134
|
+
mergedLanguageResourceBundle[namespace] = resourceKey;
|
|
130
135
|
}
|
|
131
136
|
}
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
|
|
139
|
+
mergeResourceBundle(defaultResourceBundle);
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
break;
|
|
141
|
+
// Initialize dependencies and merge their resource bundles.
|
|
142
|
+
await Promise.all(i18nDependencyInits.map(async i18nDependencyInit =>
|
|
143
|
+
i18nDependencyInit(environment, debug).then(mergeResourceBundle))
|
|
144
|
+
).then(() => {
|
|
145
|
+
let module: Module | Newable<Module> | NewableModule<Module>;
|
|
142
146
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
switch (environment) {
|
|
148
|
+
case I18nEnvironments.CLI:
|
|
149
|
+
// TODO Refactor when https://github.com/neet/i18next-cli-language-detector/issues/281 resolved.
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Per above.
|
|
151
|
+
module = I18nextCLILanguageDetector as unknown as LanguageDetectorModule;
|
|
152
|
+
break;
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
case I18nEnvironments.Browser:
|
|
155
|
+
module = I18nextBrowserLanguageDetector;
|
|
156
|
+
break;
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}).then(
|
|
158
|
+
default:
|
|
159
|
+
throw new Error("Not supported");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return module;
|
|
163
|
+
}).then(async module =>
|
|
164
|
+
i18next.use(module).init({
|
|
165
|
+
debug,
|
|
166
|
+
resources: mergedResourceBundle,
|
|
167
|
+
fallbackLng: "en",
|
|
168
|
+
defaultNS
|
|
169
|
+
})
|
|
170
|
+
).then(() => {
|
|
157
171
|
// Add toLowerCase function.
|
|
158
172
|
i18next.services.formatter?.add("toLowerCase", value => typeof value === "string" ? toLowerCase(value) : String(value));
|
|
159
173
|
});
|
|
160
174
|
}
|
|
175
|
+
|
|
176
|
+
return defaultResourceBundle;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Initialize internationalization.
|
|
181
|
+
*
|
|
182
|
+
* @param environment
|
|
183
|
+
* Environment in which the application is running.
|
|
184
|
+
*
|
|
185
|
+
* @param debug
|
|
186
|
+
* Debug setting.
|
|
187
|
+
*
|
|
188
|
+
* @returns
|
|
189
|
+
* Core resource bundle.
|
|
190
|
+
*/
|
|
191
|
+
export async function i18nCoreInit(environment: I18nEnvironment, debug = false): Promise<Resource> {
|
|
192
|
+
return i18nInit(i18nextCore, environment, debug, coreNS, coreResourceBundle);
|
|
161
193
|
}
|
package/src/logger.ts
CHANGED
|
@@ -67,10 +67,12 @@ export function logLevelOf(untypedLogLevel?: string | number): LogLevel {
|
|
|
67
67
|
* [`tslog`](https://tslog.js.org/).
|
|
68
68
|
*
|
|
69
69
|
* @param logLevel
|
|
70
|
-
* Log level as enumeration value or string. Mapped to `minLevel` in
|
|
70
|
+
* Log level as enumeration value or string. Mapped to `minLevel` and sets `hideLogPositionForProduction` to true in
|
|
71
|
+
* settings if at {@linkcode LogLevels.Info} or higher. Default is {@linkcode LogLevels.Info}.
|
|
71
72
|
*
|
|
72
73
|
* @param settings
|
|
73
|
-
* Detailed settings. See [`tslog`](https://tslog.js.org/#/?id=settings) documentation for details.
|
|
74
|
+
* Detailed settings. See [`tslog`](https://tslog.js.org/#/?id=settings) documentation for details. The `minLevel` is
|
|
75
|
+
* ignored in favour of `logLevel` but `hideLogPositionForProduction` will override the default logic.
|
|
74
76
|
*
|
|
75
77
|
* @param logObj
|
|
76
78
|
* Default log object. See [`tslog`](https://tslog.js.org/#/?id=defining-and-accessing-logobj) documentation for
|
|
@@ -83,9 +85,14 @@ export function logLevelOf(untypedLogLevel?: string | number): LogLevel {
|
|
|
83
85
|
* Log object type.
|
|
84
86
|
*/
|
|
85
87
|
export function getLogger<T extends object = object>(logLevel?: string | number, settings?: ISettingsParam<T>, logObj?: T): Logger<T> {
|
|
88
|
+
const minLevel = logLevelOf(logLevel);
|
|
89
|
+
|
|
86
90
|
return new Logger({
|
|
91
|
+
// Hiding log position for production can be overridden in settings parameter.
|
|
92
|
+
hideLogPositionForProduction: minLevel >= LogLevels.Info,
|
|
87
93
|
...settings ?? {},
|
|
88
|
-
|
|
94
|
+
// Minimum log level overrides settings parameter.
|
|
95
|
+
minLevel
|
|
89
96
|
}, logObj);
|
|
90
97
|
}
|
|
91
98
|
|