amateras 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +29 -25
  2. package/build/core.js +1 -0
  3. package/build/css.js +1 -0
  4. package/build/for.js +1 -0
  5. package/build/i18n.js +1 -0
  6. package/build/idb.js +1 -0
  7. package/build/if.js +1 -0
  8. package/build/import-map.js +1 -0
  9. package/build/markdown.js +1 -0
  10. package/build/match.js +1 -0
  11. package/build/meta.js +1 -0
  12. package/build/prefetch.js +1 -0
  13. package/build/router.js +1 -0
  14. package/build/signal.js +1 -0
  15. package/build/ui.js +1 -0
  16. package/build/widget.js +1 -0
  17. package/package.json +7 -5
  18. package/packages/core/package.json +19 -0
  19. package/packages/core/src/env.browser.ts +21 -0
  20. package/packages/core/src/env.node.ts +21 -0
  21. package/packages/core/src/global.ts +5 -0
  22. package/packages/core/src/index.ts +184 -0
  23. package/packages/core/src/lib/hmr.ts +145 -0
  24. package/packages/core/src/lib/symbols.ts +2 -0
  25. package/packages/core/src/structure/ElementProto.ts +95 -0
  26. package/packages/core/src/structure/GlobalState.ts +9 -0
  27. package/packages/core/src/structure/NodeProto.ts +18 -0
  28. package/packages/core/src/structure/Proto.ts +90 -0
  29. package/packages/core/src/structure/ProxyProto.ts +20 -0
  30. package/packages/core/src/structure/TextProto.ts +22 -0
  31. package/packages/core/src/structure/WidgetEvent.ts +17 -0
  32. package/packages/css/README.md +128 -0
  33. package/packages/css/package.json +15 -0
  34. package/packages/css/src/ext/colors/amber.ts +25 -0
  35. package/packages/css/src/ext/colors/blackwhite.ts +13 -0
  36. package/packages/css/src/ext/colors/blue.ts +25 -0
  37. package/packages/css/src/ext/colors/cyan.ts +25 -0
  38. package/packages/css/src/ext/colors/emerald.ts +25 -0
  39. package/packages/css/src/ext/colors/fuchsia.ts +25 -0
  40. package/packages/css/src/ext/colors/gray.ts +25 -0
  41. package/packages/css/src/ext/colors/green.ts +25 -0
  42. package/packages/css/src/ext/colors/indigo.ts +25 -0
  43. package/packages/css/src/ext/colors/lime.ts +25 -0
  44. package/packages/css/src/ext/colors/neutral.ts +25 -0
  45. package/packages/css/src/ext/colors/orange.ts +25 -0
  46. package/packages/css/src/ext/colors/pink.ts +25 -0
  47. package/packages/css/src/ext/colors/purple.ts +25 -0
  48. package/packages/css/src/ext/colors/red.ts +25 -0
  49. package/packages/css/src/ext/colors/rose.ts +25 -0
  50. package/packages/css/src/ext/colors/sky.ts +25 -0
  51. package/packages/css/src/ext/colors/slate.ts +25 -0
  52. package/packages/css/src/ext/colors/stone.ts +25 -0
  53. package/packages/css/src/ext/colors/teal.ts +25 -0
  54. package/packages/css/src/ext/colors/violet.ts +25 -0
  55. package/packages/css/src/ext/colors/yellow.ts +25 -0
  56. package/packages/css/src/ext/colors/zinc.ts +25 -0
  57. package/packages/css/src/ext/colors.ts +23 -0
  58. package/packages/css/src/ext/keyframes.ts +37 -0
  59. package/packages/css/src/ext/property.ts +68 -0
  60. package/packages/css/src/ext/variable.ts +51 -0
  61. package/packages/css/src/index.ts +103 -0
  62. package/packages/css/src/lib/cache.ts +27 -0
  63. package/packages/css/src/lib/colorAssign.ts +6 -0
  64. package/packages/css/src/lib/createRule.ts +31 -0
  65. package/packages/css/src/lib/utils.ts +1 -0
  66. package/packages/css/src/structure/$CSS.ts +4 -0
  67. package/packages/css/src/structure/$CSSKeyframes.ts +13 -0
  68. package/packages/css/src/structure/$CSSProperty.ts +21 -0
  69. package/packages/css/src/structure/$CSSRule.ts +39 -0
  70. package/packages/css/src/structure/$CSSVariable.ts +34 -0
  71. package/packages/css/src/types.ts +300 -0
  72. package/packages/for/package.json +15 -0
  73. package/packages/for/src/global.ts +7 -0
  74. package/packages/for/src/index.ts +15 -0
  75. package/packages/for/src/structure/For.ts +74 -0
  76. package/packages/hmr/package.json +13 -0
  77. package/packages/hmr/src/index.ts +27 -0
  78. package/packages/i18n/README.md +73 -0
  79. package/packages/i18n/package.json +15 -0
  80. package/packages/i18n/src/index.ts +78 -0
  81. package/packages/i18n/src/structure/I18n.ts +51 -0
  82. package/packages/i18n/src/structure/I18nDictionary.ts +31 -0
  83. package/packages/i18n/src/structure/I18nTranslation.ts +51 -0
  84. package/packages/i18n/src/types.ts +77 -0
  85. package/packages/idb/README.md +127 -0
  86. package/packages/idb/package.json +16 -0
  87. package/packages/idb/src/core.ts +6 -0
  88. package/packages/idb/src/index.ts +17 -0
  89. package/packages/idb/src/lib/$IDBRequest.ts +8 -0
  90. package/packages/idb/src/structure/$IDB.ts +63 -0
  91. package/packages/idb/src/structure/$IDBCursor.ts +34 -0
  92. package/packages/idb/src/structure/$IDBIndex.ts +48 -0
  93. package/packages/idb/src/structure/$IDBStore.ts +103 -0
  94. package/packages/idb/src/structure/$IDBStoreBase.ts +30 -0
  95. package/packages/idb/src/structure/$IDBTransaction.ts +38 -0
  96. package/packages/idb/src/structure/builder/$IDBBuilder.ts +229 -0
  97. package/packages/idb/src/structure/builder/$IDBStoreBuilder.ts +100 -0
  98. package/packages/if/package.json +15 -0
  99. package/packages/if/src/global.ts +15 -0
  100. package/packages/if/src/index.ts +51 -0
  101. package/packages/if/src/structure/Condition.ts +44 -0
  102. package/packages/if/src/structure/ConditionStatement.ts +25 -0
  103. package/packages/if/src/structure/Else.ts +6 -0
  104. package/packages/if/src/structure/ElseIf.ts +6 -0
  105. package/packages/if/src/structure/If.ts +6 -0
  106. package/packages/markdown/README.md +53 -0
  107. package/packages/markdown/package.json +15 -0
  108. package/packages/markdown/src/index.ts +3 -0
  109. package/packages/markdown/src/lib/type.ts +26 -0
  110. package/packages/markdown/src/lib/util.ts +21 -0
  111. package/packages/markdown/src/structure/Markdown.ts +57 -0
  112. package/packages/markdown/src/structure/MarkdownLexer.ts +111 -0
  113. package/packages/markdown/src/structure/MarkdownParser.ts +34 -0
  114. package/packages/markdown/src/syntax/alert.ts +46 -0
  115. package/packages/markdown/src/syntax/blockquote.ts +35 -0
  116. package/packages/markdown/src/syntax/bold.ts +11 -0
  117. package/packages/markdown/src/syntax/code.ts +11 -0
  118. package/packages/markdown/src/syntax/codeblock.ts +44 -0
  119. package/packages/markdown/src/syntax/heading.ts +14 -0
  120. package/packages/markdown/src/syntax/horizontalRule.ts +11 -0
  121. package/packages/markdown/src/syntax/image.ts +23 -0
  122. package/packages/markdown/src/syntax/italic.ts +11 -0
  123. package/packages/markdown/src/syntax/link.ts +46 -0
  124. package/packages/markdown/src/syntax/list.ts +121 -0
  125. package/packages/markdown/src/syntax/table.ts +67 -0
  126. package/packages/markdown/src/syntax/text.ts +19 -0
  127. package/packages/match/package.json +15 -0
  128. package/packages/match/src/global.ts +14 -0
  129. package/packages/match/src/index.ts +33 -0
  130. package/packages/match/src/structure/Case.ts +15 -0
  131. package/packages/match/src/structure/Default.ts +12 -0
  132. package/packages/match/src/structure/Match.ts +78 -0
  133. package/packages/meta/package.json +14 -0
  134. package/packages/meta/src/index.ts +36 -0
  135. package/packages/meta/src/lib/resolveMeta.ts +27 -0
  136. package/packages/meta/src/types.ts +36 -0
  137. package/packages/prefetch/package.json +14 -0
  138. package/packages/prefetch/src/index.ts +70 -0
  139. package/packages/router/README.md +18 -0
  140. package/packages/router/package.json +16 -0
  141. package/packages/router/src/global.ts +22 -0
  142. package/packages/router/src/index.ts +106 -0
  143. package/packages/router/src/structure/Link.ts +17 -0
  144. package/packages/router/src/structure/NavLink.ts +19 -0
  145. package/packages/router/src/structure/Page.ts +30 -0
  146. package/packages/router/src/structure/Route.ts +123 -0
  147. package/packages/router/src/structure/RouteGroup.ts +24 -0
  148. package/packages/router/src/structure/RouteNode.ts +54 -0
  149. package/packages/router/src/structure/RouteSlot.ts +34 -0
  150. package/packages/router/src/structure/Router.ts +192 -0
  151. package/packages/router/src/structure/RouterConstructor.ts +18 -0
  152. package/packages/router/src/types.ts +41 -0
  153. package/packages/signal/README.md +93 -0
  154. package/packages/signal/package.json +15 -0
  155. package/packages/signal/src/index.ts +97 -0
  156. package/packages/signal/src/lib/track.ts +18 -0
  157. package/packages/signal/src/structure/Signal.ts +59 -0
  158. package/packages/ui/package.json +14 -0
  159. package/packages/ui/src/index.ts +4 -0
  160. package/packages/ui/src/lib/slideshowAnimations.ts +39 -0
  161. package/packages/ui/src/structure/Radio.ts +77 -0
  162. package/packages/ui/src/structure/Slide.ts +11 -0
  163. package/packages/ui/src/structure/Slideshow.ts +99 -0
  164. package/packages/utils/package.json +18 -0
  165. package/packages/utils/src/global.ts +39 -0
  166. package/packages/utils/src/index.bun.ts +3 -0
  167. package/packages/utils/src/index.ts +2 -0
  168. package/packages/utils/src/lib/debugger.ts +14 -0
  169. package/packages/utils/src/lib/utils.ts +119 -0
  170. package/packages/utils/src/structure/UID.ts +18 -0
  171. package/packages/widget/README.md +29 -0
  172. package/packages/widget/package.json +14 -0
  173. package/packages/widget/src/index.ts +82 -0
  174. package/packages/widget/src/structure/Widget.ts +42 -0
@@ -0,0 +1,63 @@
1
+ import { _Array_from, _null, _Object_assign, _Object_fromEntries, _Promise, isBoolean } from "@amateras/utils";
2
+ import { $IDBStore, type $IDBStoreConfig } from "./$IDBStore";
3
+ import { $IDBTransaction } from "./$IDBTransaction";
4
+
5
+ export interface $IDB<Config extends $IDBConfig = any> {
6
+ /** Object with store name key and `$IDBStoreConfig` value. */
7
+ readonly stores: Config['stores'];
8
+ }
9
+ export class $IDB<Config extends $IDBConfig = any> {
10
+ /** {@link IDBDatabase} instance. */
11
+ readonly idb: IDBDatabase;
12
+ /** IndexedDB database name. */
13
+ readonly name: Config['name'];
14
+ /** IndexedDB database version. */
15
+ readonly version: Config['version'];
16
+ constructor(idb: IDBDatabase, config: Omit<Config, 'name' | 'version'>) {
17
+ this.idb = idb;
18
+ this.name = idb.name;
19
+ this.version = idb.version;
20
+ _Object_assign(this, config);
21
+ }
22
+
23
+ /**
24
+ * Create new transaction with the store name, you can directly operating the target store with `handle` function. This method will return a `Promise` with the `handle` return type value.
25
+ * @param name - Store name
26
+ * @param writable - Enable readwrite mode
27
+ * @param handle - Function execute on transaction opened
28
+ * @returns The handle function return type
29
+ */
30
+ async store<K extends keyof Config['stores'] & string, T>(name: K, handle: (store: $IDBStore<Config['stores'][K]>) => T): Promise<T>
31
+ async store<K extends keyof Config['stores'] & string, T>(name: K, writable: boolean, handle: (store: $IDBStore<Config['stores'][K]>) => T): Promise<T>
32
+ async store<K extends keyof Config['stores'] & string, T>(name: K, resolver: boolean | Function, handle?: Function) {
33
+ if (isBoolean(resolver)) return this.transaction(name, resolver, $tx => handle!($tx.store(name)));
34
+ else return this.transaction(name, $tx => resolver($tx.store(name)))
35
+ }
36
+
37
+ /**
38
+ * Create new transaction with the store name, you can pass multiple store name into `storeName` argument for operating multiple store in one transaction.
39
+ * @param store - Store name, allow string array
40
+ * @param writable - Enable readwrite mode
41
+ * @param handle - Function execute on transaction opened
42
+ * @returns The handle function return type
43
+ */
44
+ async transaction<K extends keyof Config['stores'] & string, T>(store: K | K[], handle: (transaction: $IDBTransaction<{ stores: Pick<Config['stores'], K> }>) => T): Promise<T>
45
+ async transaction<K extends keyof Config['stores'] & string, T>(store: K | K[], writable: boolean, handle: (transaction: $IDBTransaction<{ stores: Pick<Config['stores'], K> }>) => T): Promise<T>
46
+ async transaction<K extends keyof Config['stores'] & string, T>(store: K | K[], resolver?: boolean | Function, handle?: Function) {
47
+ handle = isBoolean(resolver) ? handle : resolver;
48
+ resolver = isBoolean(resolver) ? resolver : false;
49
+ const tx = this.idb.transaction(store, resolver ? 'readwrite' : 'readonly');
50
+ const $tx = new $IDBTransaction(this, tx);
51
+ const result = handle!($tx);
52
+ return new _Promise<T>((resolve, reject) => {
53
+ tx.oncomplete = _ => resolve(result);
54
+ tx.onerror = tx.onabort = _ => tx.error && reject(tx.error);
55
+ })
56
+ }
57
+ }
58
+
59
+ export type $IDBConfig = {
60
+ name: string;
61
+ version: number;
62
+ stores: { [key: string]: $IDBStoreConfig }
63
+ }
@@ -0,0 +1,34 @@
1
+ import { _instanceof, _Object_assign, _Promise } from "@amateras/utils";
2
+ import { $IDBRequest } from "#lib/$IDBRequest";
3
+ import { $IDBStore, type $IDBStoreConfig } from "./$IDBStore";
4
+ import { $IDBIndex } from "./$IDBIndex";
5
+ import type { $IDBStoreBase } from "./$IDBStoreBase";
6
+
7
+ export interface $IDBCursor {}
8
+ export class $IDBCursor<StoreConfig extends $IDBStoreConfig = any> {
9
+ #cursor: IDBCursorWithValue;
10
+ readonly store: $IDBStore<StoreConfig>;
11
+ readonly direction;
12
+ constructor(store: $IDBStoreBase, cursor: IDBCursorWithValue) {
13
+ this.#cursor = cursor;
14
+ this.store = _instanceof(store, $IDBIndex<StoreConfig>) ? store.store : store as $IDBStore;
15
+ this.direction = cursor.direction;
16
+ }
17
+
18
+ get value() { return this.#cursor.value }
19
+ get key() { return this.#cursor.key }
20
+ get primaryKey() { return this.#cursor.primaryKey }
21
+
22
+ async update<T>(value: T) {
23
+ return $IDBRequest(this.#cursor.update(value))
24
+ }
25
+
26
+ async delete() {
27
+ return $IDBRequest(this.#cursor.delete())
28
+ }
29
+
30
+ continue(key?: IDBValidKey) { this.#cursor.continue(key) }
31
+ continuePrimaryKey(key: IDBValidKey, primaryKey: IDBValidKey) { this.#cursor.continuePrimaryKey(key, primaryKey) }
32
+ advance(count: number) { this.#cursor.advance(count) }
33
+ abort() { this.#cursor.request.transaction?.abort() }
34
+ }
@@ -0,0 +1,48 @@
1
+ import { _Object_assign } from "@amateras/utils";
2
+ import type { $IDBStore, $IDBStoreConfig, QueryMultipleKeyPath } from "./$IDBStore";
3
+ import { $IDBStoreBase } from "./$IDBStoreBase";
4
+ import type { $IDBCursor } from "./$IDBCursor";
5
+
6
+ export class $IDBIndex<StoreConfig extends $IDBStoreConfig = any, Config extends $IDBIndexConfig = any> extends $IDBStoreBase{
7
+ readonly store: $IDBStore;
8
+ constructor(store: $IDBStore, index: IDBIndex, config: Config) {
9
+ super(index);
10
+ this.store = store;
11
+ _Object_assign(this, config);
12
+ }
13
+ }
14
+
15
+ export interface $IDBIndex<StoreConfig extends $IDBStoreConfig, Config extends $IDBIndexConfig = any> {
16
+ readonly unique: Config['unique'];
17
+ readonly multiEntry: Config['multiEntry'];
18
+ readonly keyPath: Config['keyPath'];
19
+
20
+ /** {@link IDBIndex.cursor} */
21
+ cursor(handle: (cursor: $IDBCursor) => void, query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<null>
22
+
23
+ /** {@link IDBIndex.keyCursor} */
24
+ keyCursor(handle: (cursor: $IDBCursor) => void, query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<null>
25
+
26
+ /** {@link IDBIndex.count} */
27
+ count(query?: $IDBIndexKey<StoreConfig, Config> | IDBKeyRange): Promise<number>;
28
+
29
+ /** {@link IDBIndex.get} */
30
+ get(query: $IDBIndexKey<StoreConfig, Config> | IDBKeyRange): Promise<StoreConfig['schema']>
31
+
32
+ /** {@link IDBIndex.getAll} */
33
+ getAll(query?: $IDBIndexKey<StoreConfig, Config> | IDBKeyRange): Promise<StoreConfig['schema'][]>
34
+ }
35
+
36
+ export type $IDBIndexConfig = {
37
+ unique: boolean;
38
+ multiEntry: boolean;
39
+ keyPath: string | string[];
40
+ name: string;
41
+ }
42
+
43
+ export type $IDBIndexKey<StoreConfig extends $IDBStoreConfig, Config extends $IDBIndexConfig> =
44
+ Config['keyPath'] extends string
45
+ ? StoreConfig['schema'][Config['keyPath']]
46
+ : Config['keyPath'] extends string[]
47
+ ? QueryMultipleKeyPath<Config['keyPath'], StoreConfig>
48
+ : IDBValidKey;
@@ -0,0 +1,103 @@
1
+ import { _instanceof, _Object_assign, _Promise } from "@amateras/utils";
2
+ import { $IDBIndex, type $IDBIndexConfig } from "./$IDBIndex";
3
+ import { $IDBRequest } from "#lib/$IDBRequest";
4
+ import { $IDBStoreBase } from "./$IDBStoreBase";
5
+ import type { $IDBCursor } from "./$IDBCursor";
6
+
7
+ export class $IDBStore<Config extends $IDBStoreConfig = any> extends $IDBStoreBase {
8
+ #store: IDBObjectStore;
9
+ constructor(store: IDBObjectStore, config: Config) {
10
+ super(store);
11
+ this.#store = store;
12
+ _Object_assign(this, config);
13
+ }
14
+
15
+ /** {@link IDBObjectStore.put} */
16
+ put<V extends $IDBStoreValueResolver<Config>>(...value: V): Promise<$IDBStoreKey<Config>>
17
+ put(value: any, key?: any) {
18
+ return $IDBRequest(this.#store.put(value, key));
19
+ }
20
+
21
+ /** {@link IDBObjectStore.add} */
22
+ add<V extends $IDBStoreValueResolver<Config>>(value: V): Promise<$IDBStoreKey<Config>>
23
+ add(value: any, key?: any) {
24
+ return $IDBRequest(this.#store.add(value, key));
25
+ }
26
+
27
+ /** {@link IDBObjectStore.delete} */
28
+ delete(query: $IDBStoreKey<Config> | IDBKeyRange): Promise<undefined>;
29
+ delete(query: IDBValidKey | IDBKeyRange) {
30
+ return $IDBRequest(this.#store.delete(query));
31
+ }
32
+
33
+ /** {@link IDBObjectStore.clear} */
34
+ clear() {
35
+ return $IDBRequest(this.#store.clear())
36
+ }
37
+
38
+ /** Get and return {@link $IDBIndex} */
39
+ index<K extends keyof Config['indexes'] & string>(name: K): $IDBIndex<Config, Config['indexes'][K]>
40
+ index(name: keyof Config['indexes'] & string) {
41
+ return new $IDBIndex(this, this.#store.index(name), this.indexes[name]!)
42
+ }
43
+ }
44
+
45
+ export interface $IDBStore<Config extends $IDBStoreConfig = any> {
46
+ readonly name: Config['name'];
47
+ readonly indexes: Config['indexes'];
48
+ readonly schema: Config['schema'];
49
+ readonly keyPath: Config['keyPath'];
50
+ readonly autoIncrement: Config['autoIncrement'];
51
+
52
+ /** {@link IDBObjectStore.cursor} */
53
+ cursor(handle: (cursor: $IDBCursor) => void, query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<null>
54
+
55
+ /** {@link IDBObjectStore.keyCursor} */
56
+ keyCursor(handle: (cursor: $IDBCursor) => void, query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<null>
57
+
58
+ /** {@link IDBObjectStore.count} */
59
+ count(query?: $IDBStoreKey<Config> | IDBKeyRange): Promise<number>;
60
+
61
+ /** {@link IDBObjectStore.get} */
62
+ get(query: $IDBStoreKey<Config> | IDBKeyRange): Promise<Config['schema']>
63
+
64
+ /** {@link IDBObjectStore.getAll} */
65
+ getAll(query?: $IDBStoreKey<Config> | IDBKeyRange): Promise<Config['schema'][]>
66
+ }
67
+
68
+ export type $IDBStoreConfig = {
69
+ name: string;
70
+ indexes: { [key: string]: $IDBIndexConfig };
71
+ schema: any;
72
+ keyPath: string | string[] | null;
73
+ autoIncrement: boolean;
74
+ }
75
+
76
+ export type $IDBStoreValueResolver<Config extends $IDBStoreConfig> =
77
+ Config['keyPath'] extends string
78
+ ? Config['autoIncrement'] extends true
79
+ ? [Omit<Config['schema'], Config['keyPath']> & {[key in Config['keyPath']]?: Config['schema'][key]}]
80
+ : [Config['schema']]
81
+ : Config['keyPath'] extends string[]
82
+ ? [Config['schema']]
83
+ : Config['autoIncrement'] extends true
84
+ ? [Config['schema']]
85
+ : [Config['schema'], IDBValidKey];
86
+
87
+ export type $IDBStoreKey<Config extends $IDBStoreConfig> =
88
+ Config['keyPath'] extends string
89
+ ? Config['schema'][Config['keyPath']]
90
+ : Config['keyPath'] extends string[]
91
+ ? QueryMultipleKeyPath<Config['keyPath'], Config>
92
+ : Config['autoIncrement'] extends true
93
+ ? number
94
+ : IDBValidKey;
95
+
96
+ export type QueryMultipleKeyPath<T extends string[], Config extends { schema: {} }> =
97
+ T extends [infer A, ...infer Rest]
98
+ ? A extends keyof Config['schema']
99
+ ? Rest extends string[]
100
+ ? [Config['schema'][A], ...QueryMultipleKeyPath<Rest, Config>]
101
+ : [Config['schema'][A]]
102
+ : never
103
+ : []
@@ -0,0 +1,30 @@
1
+ import { _Array_from, _instanceof, _null, _Promise } from "@amateras/utils";
2
+ import { $IDBRequest } from "#lib/$IDBRequest";
3
+ import { $IDBCursor } from "./$IDBCursor";
4
+
5
+ export abstract class $IDBStoreBase {
6
+ readonly #instance;
7
+ constructor(instance: IDBObjectStore | IDBIndex) {
8
+ this.#instance = instance;
9
+ }
10
+
11
+ cursor(handle: (cursor: $IDBCursor) => void, query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection) {
12
+ return $IDBRequest(this.#instance.openCursor(query, direction), (req , resolve) => req.result ? handle(new $IDBCursor(this, req.result)) : resolve(null))
13
+ }
14
+
15
+ keyCursor(handle: (cursor: $IDBCursor) => void, query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection) {
16
+ return $IDBRequest(this.#instance.openCursor(query, direction), (req , resolve) => req.result ? handle(new $IDBCursor(this, req.result)) : resolve(null))
17
+ }
18
+
19
+ count(query?: IDBValidKey | IDBKeyRange) {
20
+ return $IDBRequest(this.#instance.count(query));
21
+ }
22
+
23
+ get(query: IDBValidKey | IDBKeyRange) {
24
+ return $IDBRequest(this.#instance.get(query))
25
+ }
26
+
27
+ getAll(query?: IDBValidKey | IDBKeyRange) {
28
+ return $IDBRequest(this.#instance.getAll(query))
29
+ }
30
+ }
@@ -0,0 +1,38 @@
1
+ import { _Array_from, _Object_assign, forEach } from "@amateras/utils";
2
+ import type { $IDB } from "./$IDB";
3
+ import { $IDBStore, type $IDBStoreConfig } from "./$IDBStore";
4
+
5
+ export interface $IDBTransaction {}
6
+ export class $IDBTransaction<Config extends $IDBTransactionConfig = any> {
7
+ #transaction: IDBTransaction;
8
+ #$idb: $IDB
9
+ readonly writable: boolean;
10
+ readonly stores: Config['stores'] = {};
11
+ readonly durability: string;
12
+ constructor($idb: $IDB, transaction: IDBTransaction) {
13
+ this.#transaction = transaction;
14
+ this.#$idb = $idb;
15
+ this.writable = transaction.mode !== 'readonly';
16
+ this.durability = transaction.durability;
17
+ forEach(_Array_from(transaction.objectStoreNames), name => {
18
+ _Object_assign(this.stores, { [name]: $idb.stores[name] })
19
+ })
20
+ }
21
+
22
+ store<N extends keyof Config['stores'] & string>(name: N): $IDBStore<Config['stores'][N]>
23
+ store(name: string) {
24
+ return new $IDBStore(this.#transaction.objectStore(name), this.#$idb.stores[name]);
25
+ }
26
+
27
+ commit() {
28
+ return this.#transaction.commit();
29
+ }
30
+
31
+ abort() {
32
+ return this.#transaction.abort();
33
+ }
34
+ }
35
+
36
+ export type $IDBTransactionConfig = {
37
+ stores: { [key: string]: $IDBStoreConfig }
38
+ }
@@ -0,0 +1,229 @@
1
+ import { $IDB, type $IDBConfig } from "#structure/$IDB";
2
+ import { _Array_from, _instanceof, _JSON_stringify, _null, _Object_assign, _Object_fromEntries, _Promise, forEach, isFunction, trycatch } from "@amateras/utils";
3
+ import { $IDBStoreBuilder } from "./$IDBStoreBuilder";
4
+ import type { $IDBIndexConfig } from "#structure/$IDBIndex";
5
+ import type { $IDBStoreConfig } from "#structure/$IDBStore";
6
+ // optimizer variables
7
+ const objectStoreNames = 'objectStoreNames';
8
+ const deleteObjectStore = 'deleteObjectStore';
9
+ const createObjectStore = 'createObjectStore';
10
+ const _indexedDB = indexedDB;
11
+ const onupgradeneeded = 'onupgradeneeded';
12
+ const onsuccess = 'onsuccess';
13
+
14
+ export interface $IDBBuilder {
15
+ readonly name: string;
16
+ readonly version: number;
17
+ }
18
+ export class $IDBBuilder<Config extends $IDBConfig = { name: string, stores: {}, version: number }> {
19
+ #deleteUnused = false;
20
+ storeMap = new Map<string, $IDBStoreBuilder>();
21
+ #devMode: boolean = false;
22
+ constructor(config: Config) {
23
+ _Object_assign(this, config);
24
+ }
25
+
26
+ /**
27
+ * This option helping developer to debug when initializing.
28
+ * @param dev - Enable dev mode
29
+ */
30
+ devMode(dev: boolean) {
31
+ this.#devMode = dev;
32
+ return this;
33
+ }
34
+
35
+ /**
36
+ * If set to true, unused store will be deleted when initialize.
37
+ * @param enable - Enable delete unused stores
38
+ */
39
+ deleteUnused(enable: boolean) {
40
+ this.#deleteUnused = enable;
41
+ return this;
42
+ }
43
+
44
+ /**
45
+ * Add new store to builder.
46
+ * @param name - Store name
47
+ * @param builder - Store builder or builder function
48
+ */
49
+ store<N extends string, B extends $IDBStoreBuilderFunction>(name: N, builder: B): $IDBBuilder<Prettify<Config & { stores: Config['stores'] & Prettify<Record<N, ReturnType<B>['config']>> }>>
50
+ store<N extends string, B extends $IDBStoreBuilder<any>>(name: N, builder: B): $IDBBuilder<Prettify<Config & { stores: Config['stores'] & Prettify<Record<N, B['config']>> }>>
51
+ store(name: string, builder: $IDBStoreBuilderFunction | $IDBStoreBuilder)
52
+ {
53
+ this.storeMap.set(name, isFunction(builder) ? builder(new $IDBStoreBuilder({autoIncrement: false, keyPath: null, indexes: {}, name, schema: null})) : builder);
54
+ return this as any;
55
+ }
56
+
57
+ /**
58
+ * Open IDB and initialize, create new IDB if the name of IDB is not exists, or perform the upgrade if version number change.
59
+ */
60
+ async open(): Promise<$IDB<Config>> {
61
+ return new _Promise<$IDB>((resolve, reject) => {
62
+ const {version: dbVersion, name: dbName, storeMap} = this;
63
+ const initDBRequest = _indexedDB.open(dbName);
64
+ const createStoresMap = new Map<string, $IDBStoreBuilder<$IDBStoreConfig>>();
65
+ const createIndexMap = new Map<$IDBStoreBuilder, Map<string, $IDBIndexConfig>>();
66
+ const upgradeStoreMap = new Map<string, $IDBStoreBuilder<$IDBStoreConfig>>();
67
+ const cachedObjectMap = new Map<string, {key: any, value: any}[]>();
68
+ const unusedStoreNameList: string[] = [];
69
+ const debug = (message: string) => this.#devMode && console.debug(`[$IDBBuilder (${dbName})]`, message);
70
+ const storesObject: $IDBConfig['stores'] = _Object_fromEntries(_Array_from(storeMap).map(([name, {config: {keyPath, autoIncrement}, indexes}]) => [
71
+ name,
72
+ {
73
+ autoIncrement, keyPath, name,
74
+ schema: _null,
75
+ indexes: _Object_fromEntries(_Array_from(indexes).map(([name, {keyPath, multiEntry, unique}]) => [
76
+ name,
77
+ { keyPath, multiEntry, unique } as $IDBIndexConfig
78
+ ]))
79
+ } as $IDBStoreConfig
80
+ ]))
81
+ const idbConfig = { version: dbVersion, name: dbName, stores: storesObject };
82
+ /** IndexedDB initial create function */
83
+ const initialCreateDB = () => {
84
+ debug(`No IDB detected, create IDB`);
85
+ const {transaction, result: idb} = initDBRequest;
86
+ forEach(storeMap, ([name, storeBuilder]) => {
87
+ createStoresMap.set(name, storeBuilder);
88
+ createIndexMap.set(storeBuilder, new Map(storeBuilder.indexes))
89
+ })
90
+ if (idb.version === dbVersion) upgradeStore(initDBRequest);
91
+ else transaction!.oncomplete = _ => {
92
+ const upgradeDBRequest = indexedDB.open(dbName, dbVersion);
93
+ upgradeDBRequest.onupgradeneeded = _ => upgradeStore(upgradeDBRequest);
94
+ }
95
+ }
96
+ /** IndexedDB initial open function */
97
+ const initialOpenDB = async () => {
98
+ debug(`IDB Detected`);
99
+ const idb = initDBRequest.result;
100
+ const $idb = new $IDB(idb, idbConfig);
101
+ const transaction = idb[objectStoreNames].length ? idb.transaction(_Array_from(idb[objectStoreNames]), 'readonly') : null;
102
+ const noUpgrade = () => {
103
+ debug(`No Upgrade`);
104
+ resolve($idb);
105
+ }
106
+ if (idb.version === dbVersion) return noUpgrade();
107
+ // get unused stores
108
+ transaction && forEach(_Array_from(transaction[objectStoreNames]), name => storeMap.has(name) && unusedStoreNameList.push(name))
109
+ // check store config matches
110
+ forEach(storeMap, ([storeName, storeBuilder]) => {
111
+ const {keyPath, autoIncrement} = storeBuilder.config;
112
+ const indexMap = new Map();
113
+ const checkIndexes = () =>
114
+ forEach(storeBuilder.indexes, ([indexName, indexBuilder]) => {
115
+ const [index] = trycatch(() => store?.index(indexName));
116
+ const CONFIG_CHANGED = _JSON_stringify(indexBuilder.keyPath) !== _JSON_stringify(index?.keyPath)
117
+ || !!indexBuilder.multiEntry !== index?.multiEntry
118
+ || !!indexBuilder.unique !== index?.unique;
119
+ if (!index || CONFIG_CHANGED) {
120
+ indexMap.set(indexName, indexBuilder);
121
+ createIndexMap.set(storeBuilder, indexMap);
122
+ }
123
+ })
124
+ // get store from idb
125
+ const [store] = trycatch(() => transaction?.objectStore(storeName));
126
+ // create store and break if idb have no store exist
127
+ if (!store) return createStoresMap.set(storeName, storeBuilder), checkIndexes();
128
+ // define matches variables
129
+ const OBJECT_UPGRADE = _Array_from(storeBuilder.upgrades).find(([upgradeVersion],) =>
130
+ dbVersion >= upgradeVersion && idb.version < upgradeVersion
131
+ )
132
+ const CONFIG_CHANGED =
133
+ _JSON_stringify(keyPath) !== _JSON_stringify(store.keyPath)
134
+ || autoIncrement !== store?.autoIncrement
135
+ const UPGRADE_NEEDED = OBJECT_UPGRADE || CONFIG_CHANGED;
136
+ // add indexes
137
+ checkIndexes();
138
+ // store existed and not need upgrade
139
+ if (store && !UPGRADE_NEEDED) return;
140
+ // add upgrade store queue
141
+ upgradeStoreMap.set(storeName, storeBuilder)
142
+
143
+ })
144
+ // resolve if no need upgrade
145
+ if (dbVersion === idb.version && !createStoresMap.size && !upgradeStoreMap.size && !unusedStoreNameList.length && !createIndexMap.size)
146
+ return noUpgrade();
147
+ // cache objects
148
+ for (const [storeName, storeBuilder] of upgradeStoreMap) {
149
+ const cache: {key: any, value: any}[] = [];
150
+ // filter version lower than current idb
151
+ const upgradeHandleList = _Array_from(storeBuilder.upgrades)
152
+ .filter(([upgradeVersion]) => dbVersion >= upgradeVersion && idb.version < upgradeVersion )
153
+ .sort((a, b) => a[0] - b[0])
154
+ .map(config => config[1]);
155
+ // cache objects from store
156
+ await $idb.transaction(storeName, false, async $tx => {
157
+ cachedObjectMap.set(storeName, cache);
158
+ await $tx.store(storeName).cursor(async cursor => {
159
+ cache.push({key: cursor.key, value: cursor.value});
160
+ cursor.continue();
161
+ })
162
+ })
163
+ // upgrade objects
164
+ for (const upgradeHandle of upgradeHandleList)
165
+ cachedObjectMap.set(storeName, await upgradeHandle(cache, $idb));
166
+ }
167
+ // upgrade db from lower version
168
+ idb.close();
169
+ const upgradeDBRequest = _indexedDB.open(dbName, dbVersion);
170
+ upgradeDBRequest[onupgradeneeded] = _ => upgradeStore(upgradeDBRequest);
171
+ upgradeDBRequest[onsuccess] = _ => {
172
+ debug('IDB Upgrade Completed');
173
+ resolve(new $IDB(upgradeDBRequest.result, idbConfig));
174
+ }
175
+ }
176
+
177
+ /** IndexedDB upgrade version */
178
+ const upgradeStore = (req: IDBOpenDBRequest) => {
179
+ debug('Upgrade DB')
180
+ const idb = req.result;
181
+ /** 'versionchange' type transaction */
182
+ const transaction = req.transaction as IDBTransaction;
183
+ // create stores
184
+ forEach(createStoresMap, ([name, {config}]) => {
185
+ idb[createObjectStore](name, config);
186
+ debug(`Store Created: ${name}`);
187
+ });
188
+ // upgrade stores
189
+ forEach(upgradeStoreMap, ([name, {config}]) => {
190
+ idb[deleteObjectStore](name);
191
+ idb[createObjectStore](name, config);
192
+ debug(`Store Upgraded: ${name}`);
193
+ })
194
+ // create indexes
195
+ forEach(createIndexMap, ([{config: {name}}, indexMap]) => {
196
+ const store = transaction.objectStore(name);
197
+ forEach(indexMap, ([indexName, {keyPath, ...config}]) => {
198
+ // if indexes existed, delete and create again
199
+ if (store.indexNames.contains(indexName)) store.deleteIndex(indexName);
200
+ store.createIndex(indexName, keyPath, config);
201
+ debug(`Store '${name}' Index Created: ${indexName}`);
202
+ })
203
+ })
204
+ // delete unused stores
205
+ if (this.#deleteUnused) forEach(unusedStoreNameList, name => {
206
+ idb[deleteObjectStore](name);
207
+ debug(`Unused Store Deleted: ${name}`);
208
+ });
209
+ // open db again for insert objects
210
+ forEach(cachedObjectMap, ([storeName, objectList]) => {
211
+ const store = transaction.objectStore(storeName);
212
+ forEach(objectList, ({key, value}) => {
213
+ if (store.autoIncrement || store.keyPath) store.add(value);
214
+ else store.add(value, key)
215
+ })
216
+ debug(`Recovered Store Objects: ${objectList.length} objects of store '${storeName}'`);
217
+ })
218
+ }
219
+
220
+ // If db not exist, create db will trigger upgraedneeded event
221
+ initDBRequest[onupgradeneeded] = initialCreateDB;
222
+ // If db exist, trigger success event
223
+ initDBRequest[onsuccess] = initialOpenDB;
224
+ initDBRequest.onerror = _ => reject(initDBRequest.error);
225
+ })
226
+ }
227
+ }
228
+
229
+ export type $IDBStoreBuilderFunction = (store: $IDBStoreBuilder) => $IDBStoreBuilder<any>;
@@ -0,0 +1,100 @@
1
+ import type { $IDB } from "#structure/$IDB";
2
+ import { _null } from "@amateras/utils";
3
+ import type { $IDBStoreConfig } from "#structure/$IDBStore";
4
+ import type { $IDBIndexConfig } from "#structure/$IDBIndex";
5
+
6
+ export class $IDBStoreBuilder<Config extends $IDBStoreConfig = { name: string, keyPath: null, autoIncrement: false, schema: any, indexes: {} }> {
7
+ readonly config: Config
8
+ upgrades = new Map<number, $IDBStoreUpgradeFunction>();
9
+ indexes = new Map<string, $IDBIndexConfig>();
10
+ constructor(config: Config) {
11
+ this.config = config;
12
+ }
13
+
14
+ /**
15
+ * Define the `keyPath` option of store.
16
+ *
17
+ * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB#structuring_the_database).
18
+ */
19
+ keyPath<K extends string[]>(keyPath: K): $IDBStoreBuilder<Prettify<Omit<Config, 'keyPath'> & { keyPath: K }>>;
20
+ keyPath<K extends string>(keyPath: K): $IDBStoreBuilder<Prettify<Omit<Config, 'keyPath'> & { keyPath: K }>>;
21
+ keyPath(keyPath: string | string[]) {
22
+ this.config.keyPath = keyPath;
23
+ return this as any;
24
+ }
25
+
26
+ /**
27
+ * Define the `autoIncrement` option of store.
28
+ *
29
+ * [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB#structuring_the_database).
30
+ */
31
+ autoIncrement<K extends boolean>(enable: K): $IDBStoreBuilder<Prettify<Omit<Config, 'autoIncrement'> & { autoIncrement: K }>>;
32
+ autoIncrement(enable: boolean) {
33
+ this.config.autoIncrement = enable;
34
+ return this as any;
35
+ }
36
+
37
+ /**
38
+ * Use the generic type to define store object type.
39
+ *
40
+ * @example
41
+ * store.schema<{
42
+ * id: number,
43
+ * name: string,
44
+ * created: Date,
45
+ * updated: Date
46
+ * }>()
47
+ */
48
+ schema<T extends $IDBStoreBuilderSchema<Config>>(): $IDBStoreBuilder<Prettify<Omit<Config, 'schema'> & { schema: T }>>;
49
+ schema() { return this as any }
50
+
51
+ /**
52
+ * Add new index to store builder.
53
+ * @param name - Index name
54
+ * @param config - {@link $IDBIndexOptionalConfig}
55
+ */
56
+ index<N extends string, C extends $IDBIndexOptionalConfig<Config>>(name: N, config: C): $IDBStoreBuilder<Prettify<Config & { indexes: Config['indexes'] & Prettify<Record<N, Prettify<$IDBIndexOptionalHandle<N, C>>>> }>>
57
+ index<C extends $IDBIndexConfig>(name: string, config: C) {
58
+ this.indexes.set(name, {
59
+ ...config, name,
60
+ multiEntry: config.multiEntry ?? false,
61
+ unique: config.unique ?? false
62
+ });
63
+ return this as any;
64
+ }
65
+
66
+ /**
67
+ * Add store upgrade function to store builder. The store upgrade function is used for change object structure when the store is upgrading.
68
+ * @param version - Target version of database
69
+ * @param handle - Upgrade handle function
70
+ */
71
+ upgrade(version: number, handle: $IDBStoreUpgradeFunction) {
72
+ this.upgrades.set(version, handle);
73
+ return this;
74
+ }
75
+
76
+ }
77
+
78
+ export type $IDBStoreUpgradeFunction = (objects: {key: IDBValidKey, value: any}[], idb: $IDB<any>) => OrPromise<{key: IDBValidKey, value: any}[]>;
79
+
80
+ export type $IDBIndexOptionalConfig<StoreConfig extends $IDBStoreConfig = any> = {
81
+ keyPath: OrArray<keyof StoreConfig['schema']>,
82
+ unique?: boolean,
83
+ multiEntry?: boolean
84
+ }
85
+
86
+ type $IDBIndexOptionalHandle<N extends string, Config extends $IDBIndexOptionalConfig> = {
87
+ name: N;
88
+ keyPath: Config['keyPath'];
89
+ multiEntry: Config['multiEntry'] extends boolean ? Config['multiEntry'] : false;
90
+ unique: Config['unique'] extends boolean ? Config['unique'] : false;
91
+ }
92
+
93
+ type $IDBStoreBuilderSchema<Config extends $IDBStoreConfig> =
94
+ Config['keyPath'] extends string
95
+ ? Config['autoIncrement'] extends true
96
+ ? { [key in Config['keyPath']]: number }
97
+ : { [key in Config['keyPath']]: IDBValidKey }
98
+ : Config['keyPath'] extends string[]
99
+ ? { [key in Config['keyPath'][number]]: IDBValidKey }
100
+ : any;
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@amateras/if",
3
+ "peerDependencies": {
4
+ "@amateras/core": "workspace:*",
5
+ "@amateras/signal": "workspace:*",
6
+ "@amateras/utils": "workspace:*"
7
+ },
8
+ "imports": {
9
+ "#structure/*": "./src/structure/*.ts",
10
+ "#lib/*": "./src/lib/*.ts"
11
+ },
12
+ "exports": {
13
+ ".": "./src/index.ts"
14
+ }
15
+ }