@fumari/stf 0.0.1 → 0.0.3
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/dist/index.d.ts +3 -5
- package/dist/index.js +13 -14
- package/dist/lib/utils.d.ts +2 -4
- package/dist/lib/utils.js +50 -1
- package/dist/{types-PXiOOMNs.d.ts → types-Do_aTV5I.d.ts} +1 -2
- package/package.json +4 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/utils.d.ts.map +0 -1
- package/dist/types-PXiOOMNs.d.ts.map +0 -1
- package/dist/utils-W82BoH4I.js +0 -53
- package/dist/utils-W82BoH4I.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as FieldKey } from "./types-
|
|
1
|
+
import { t as FieldKey } from "./types-Do_aTV5I.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -117,13 +117,11 @@ declare function useFieldValue<V = unknown>(key: FieldKey, options?: {
|
|
|
117
117
|
*
|
|
118
118
|
* to re-compute on in-place updates (may happen on objects, arrays), you should clone the object here.
|
|
119
119
|
*/
|
|
120
|
-
compute?: (currentValue: unknown) => V;
|
|
121
|
-
/** determine whether the value/computed value is changed */
|
|
120
|
+
compute?: (currentValue: unknown) => V; /** determine whether the value/computed value is changed */
|
|
122
121
|
isChanged?: (prev: V, next: V) => boolean;
|
|
123
122
|
}): readonly [V, (newValue: unknown) => boolean];
|
|
124
123
|
declare function useListener(listener: DataEngineListener & {
|
|
125
124
|
stf?: Stf;
|
|
126
125
|
}): void;
|
|
127
126
|
//#endregion
|
|
128
|
-
export { ArrayItemInfo, DataEngine, DataEngineListener, DefaultValue, FieldKey, PropertyItemInfo, Stf, StfProvider, useArray, useDataEngine, useFieldValue, useListener, useObject, useStf };
|
|
129
|
-
//# sourceMappingURL=index.d.ts.map
|
|
127
|
+
export { ArrayItemInfo, DataEngine, DataEngineListener, DefaultValue, FieldKey, PropertyItemInfo, Stf, StfProvider, useArray, useDataEngine, useFieldValue, useListener, useObject, useStf };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { deepEqual, objectGet, objectSet, stringifyFieldKey } from "./lib/utils.js";
|
|
4
4
|
import { createContext, use, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
|
|
@@ -26,12 +26,12 @@ function useArray(field, options = {}) {
|
|
|
26
26
|
const [items] = useFieldValue(field, {
|
|
27
27
|
defaultValue: options.defaultValue,
|
|
28
28
|
compute(value) {
|
|
29
|
-
const items
|
|
30
|
-
if (Array.isArray(value)) for (let i = 0; i < value.length; i++) items
|
|
29
|
+
const items = [];
|
|
30
|
+
if (Array.isArray(value)) for (let i = 0; i < value.length; i++) items.push({
|
|
31
31
|
field: [...field, i],
|
|
32
32
|
index: i
|
|
33
33
|
});
|
|
34
|
-
return items
|
|
34
|
+
return items;
|
|
35
35
|
},
|
|
36
36
|
isChanged(prev, next) {
|
|
37
37
|
return prev.length !== next.length;
|
|
@@ -118,12 +118,12 @@ function getDefaultValue(defaultValue) {
|
|
|
118
118
|
}
|
|
119
119
|
var ListenerManager = class {
|
|
120
120
|
constructor() {
|
|
121
|
-
this.
|
|
121
|
+
this.unindexed = /* @__PURE__ */ new Set();
|
|
122
122
|
this.indexed = /* @__PURE__ */ new Map();
|
|
123
123
|
}
|
|
124
124
|
add(listener) {
|
|
125
125
|
if (!listener.field) {
|
|
126
|
-
this.
|
|
126
|
+
this.unindexed.add(listener);
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
const key = stringifyFieldKey(listener.field);
|
|
@@ -132,27 +132,27 @@ var ListenerManager = class {
|
|
|
132
132
|
this.indexed.set(key, set);
|
|
133
133
|
}
|
|
134
134
|
remove(listener) {
|
|
135
|
-
if (!listener.field) this.
|
|
135
|
+
if (!listener.field) this.unindexed.delete(listener);
|
|
136
136
|
else this.indexed.get(stringifyFieldKey(listener.field))?.delete(listener);
|
|
137
137
|
}
|
|
138
138
|
onUpdate(field, ctx) {
|
|
139
|
-
for (const v of this.
|
|
139
|
+
for (const v of this.unindexed) v.onUpdate?.(field, ctx);
|
|
140
140
|
const updatedKey = stringifyFieldKey(field);
|
|
141
141
|
if (ctx.swallow) {
|
|
142
142
|
const set = this.indexed.get(updatedKey);
|
|
143
143
|
if (set) for (const v of set) v.onUpdate?.(field, ctx);
|
|
144
144
|
} else for (const [k, listeners] of this.indexed.entries()) {
|
|
145
|
-
if (k !== updatedKey && !k.startsWith(updatedKey + ".")) continue;
|
|
145
|
+
if (updatedKey.length !== 0 && k !== updatedKey && !k.startsWith(updatedKey + ".")) continue;
|
|
146
146
|
for (const v of listeners) v.onUpdate?.(field, ctx);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
onInit(field) {
|
|
150
|
-
for (const v of this.
|
|
150
|
+
for (const v of this.unindexed) v.onInit?.(field);
|
|
151
151
|
const set = this.indexed.get(stringifyFieldKey(field));
|
|
152
152
|
if (set) for (const v of set) v.onInit?.(field);
|
|
153
153
|
}
|
|
154
154
|
onDelete(field) {
|
|
155
|
-
for (const v of this.
|
|
155
|
+
for (const v of this.unindexed) v.onDelete?.(field);
|
|
156
156
|
const set = this.indexed.get(stringifyFieldKey(field));
|
|
157
157
|
if (set) for (const v of set) v.onDelete?.(field);
|
|
158
158
|
}
|
|
@@ -249,8 +249,8 @@ var DataEngine = class {
|
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
251
|
reset(data) {
|
|
252
|
-
this.update([], data);
|
|
253
252
|
this.attachedDataMap.clear();
|
|
253
|
+
this.update([], data);
|
|
254
254
|
}
|
|
255
255
|
};
|
|
256
256
|
function useFieldValue(key, options = {}) {
|
|
@@ -295,5 +295,4 @@ function useListener(listener) {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
//#endregion
|
|
298
|
-
export { DataEngine, StfProvider, useArray, useDataEngine, useFieldValue, useListener, useObject, useStf };
|
|
299
|
-
//# sourceMappingURL=index.js.map
|
|
298
|
+
export { DataEngine, StfProvider, useArray, useDataEngine, useFieldValue, useListener, useObject, useStf };
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { t as FieldKey } from "../types-
|
|
1
|
+
import { t as FieldKey } from "../types-Do_aTV5I.js";
|
|
2
2
|
|
|
3
3
|
//#region src/lib/utils.d.ts
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* test if array a starts with array b, only compare values via `===`.
|
|
7
6
|
*/
|
|
@@ -19,5 +18,4 @@ declare function objectSet(obj: unknown, key: FieldKey, value: unknown): unknown
|
|
|
19
18
|
declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
20
19
|
declare function stringifyFieldKey(fieldKey: FieldKey): string;
|
|
21
20
|
//#endregion
|
|
22
|
-
export { arrayStartsWith, deepEqual, objectGet, objectSet, stringifyFieldKey };
|
|
23
|
-
//# sourceMappingURL=utils.d.ts.map
|
|
21
|
+
export { arrayStartsWith, deepEqual, objectGet, objectSet, stringifyFieldKey };
|
package/dist/lib/utils.js
CHANGED
|
@@ -1,3 +1,52 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/lib/utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* test if array a starts with array b, only compare values via `===`.
|
|
4
|
+
*/
|
|
5
|
+
function arrayStartsWith(a, b) {
|
|
6
|
+
if (b.length > a.length) return false;
|
|
7
|
+
for (let i = 0; i < b.length; i++) if (a[i] !== b[i]) return false;
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
function objectGet(obj, key) {
|
|
11
|
+
let cur = obj;
|
|
12
|
+
for (const prop of key) {
|
|
13
|
+
if (typeof cur !== "object" || cur === null || !(prop in cur)) return;
|
|
14
|
+
cur = cur[prop];
|
|
15
|
+
}
|
|
16
|
+
return cur;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* set the value of field if it exists (in place)
|
|
20
|
+
*
|
|
21
|
+
* @returns updated value, throw error if parent object doesn't exist
|
|
22
|
+
*/
|
|
23
|
+
function objectSet(obj, key, value) {
|
|
24
|
+
if (key.length === 0) return value;
|
|
25
|
+
const parent = objectGet(obj, key.slice(0, -1));
|
|
26
|
+
if (typeof parent !== "object" || parent === null) throw new Error("missing parent object");
|
|
27
|
+
parent[key[key.length - 1]] = value;
|
|
28
|
+
return obj;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* doesn't handle recursive objects
|
|
32
|
+
*/
|
|
33
|
+
function deepEqual(a, b) {
|
|
34
|
+
if (a === b) return true;
|
|
35
|
+
if (a == null || b == null) return false;
|
|
36
|
+
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
37
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
38
|
+
if (a.length !== b.length) return false;
|
|
39
|
+
return a.every((item, index) => deepEqual(item, b[index]));
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(a) || Array.isArray(b)) return false;
|
|
42
|
+
const keysA = Object.keys(a);
|
|
43
|
+
const keysB = Object.keys(b);
|
|
44
|
+
if (keysA.length !== keysB.length) return false;
|
|
45
|
+
return keysA.every((key) => Object.prototype.hasOwnProperty.call(b, key) && deepEqual(a[key], b[key]));
|
|
46
|
+
}
|
|
47
|
+
function stringifyFieldKey(fieldKey) {
|
|
48
|
+
return fieldKey.map((v) => `${typeof v}:${v}`).join(".");
|
|
49
|
+
}
|
|
2
50
|
|
|
51
|
+
//#endregion
|
|
3
52
|
export { arrayStartsWith, deepEqual, objectGet, objectSet, stringifyFieldKey };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fumari/stf",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Schema to Form.",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"access": "public"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@types/node": "25.
|
|
23
|
-
"@types/react": "^19.2.
|
|
24
|
-
"tsdown": "^0.
|
|
22
|
+
"@types/node": "25.2.1",
|
|
23
|
+
"@types/react": "^19.2.13",
|
|
24
|
+
"tsdown": "^0.20.3",
|
|
25
25
|
"eslint-config-custom": "0.0.0",
|
|
26
26
|
"tsconfig": "0.0.0"
|
|
27
27
|
},
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/lib/stf.tsx","../src/lib/data-engine.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["items"],"sources":["../src/lib/stf.tsx","../src/lib/data-engine.ts"],"sourcesContent":["import { createContext, ReactNode, use, useMemo } from 'react';\nimport { DataEngine, DefaultValue, useFieldValue } from './data-engine';\nimport { FieldKey } from './types';\nimport { deepEqual } from './utils';\n\nconst Context = createContext<Stf | null>(null);\n\nexport interface Stf {\n dataEngine: DataEngine;\n}\n\nexport function StfProvider({ value, children }: { value: Stf; children: ReactNode }) {\n return <Context value={value}>{children}</Context>;\n}\n\nexport function useStf(options: {\n /**\n * Note: the passed object will be modified in place, use `structuredClone()` to keep the original object unchanged.\n */\n defaultValues?: DefaultValue<Record<string, unknown>>;\n}): Stf {\n const { defaultValues } = options;\n\n const dataEngine = useMemo(\n () => new DataEngine(defaultValues),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- assume unchanged\n [],\n );\n return useMemo(\n () => ({\n dataEngine,\n }),\n [dataEngine],\n );\n}\n\nexport function useDataEngine(stf?: Stf) {\n if (stf) return stf.dataEngine;\n return use(Context)!.dataEngine;\n}\n\nexport interface ArrayItemInfo {\n field: FieldKey;\n index: number;\n}\n\nexport function useArray(\n field: FieldKey,\n options: { defaultValue?: DefaultValue<unknown[]> } = {},\n) {\n const engine = useDataEngine();\n const [items] = useFieldValue(field, {\n defaultValue: options.defaultValue,\n compute(value) {\n const items: ArrayItemInfo[] = [];\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n items.push({\n field: [...field, i],\n index: i,\n });\n }\n }\n return items;\n },\n isChanged(prev, next) {\n return prev.length !== next.length;\n },\n });\n\n return {\n items,\n insertItem(itemValue?: unknown) {\n const value = engine.get(field);\n\n engine.update(field, Array.isArray(value) ? [...value, itemValue] : [itemValue]);\n },\n removeItem(index: number) {\n engine.delete([...field, index]);\n },\n };\n}\nexport type PropertyItemInfo<T> =\n | {\n kind: 'fixed' | 'fallback';\n field: FieldKey;\n key: string;\n info: T;\n }\n | {\n kind: 'pattern';\n field: FieldKey;\n key: string;\n pattern: string;\n info: T;\n };\n\nexport function useObject<T>(\n field: FieldKey,\n options: {\n defaultValue?: DefaultValue<object>;\n properties: Record<string, T>;\n patternProperties?: Record<string, T>;\n fallback?: T;\n },\n) {\n const engine = useDataEngine();\n const [objectKeys] = useFieldValue(field, {\n defaultValue: options.defaultValue,\n compute(currentValue) {\n return currentValue ? Object.keys(currentValue) : [];\n },\n isChanged(prev, next) {\n return !deepEqual(prev, next);\n },\n });\n\n const properties = useMemo(() => {\n const properties: PropertyItemInfo<T>[] = [];\n const unknownKeys = new Set(objectKeys);\n for (const [key, prop] of Object.entries(options.properties)) {\n unknownKeys.delete(key);\n properties.push({\n kind: 'fixed',\n field: [...field, key],\n key,\n info: prop,\n });\n }\n\n for (const [pattern, prop] of Object.entries(options.patternProperties ?? {})) {\n const regex = RegExp(pattern);\n\n for (const key of unknownKeys) {\n if (!key.match(regex)) continue;\n unknownKeys.delete(key);\n properties.push({\n kind: 'pattern',\n info: prop,\n key,\n pattern,\n field: [...field, key],\n });\n }\n }\n\n if (options.fallback) {\n for (const key of unknownKeys) {\n properties.push({\n kind: 'fallback',\n field: [...field, key],\n key,\n info: options.fallback,\n });\n }\n }\n\n return properties;\n }, [field, objectKeys, options.fallback, options.patternProperties, options.properties]);\n\n return {\n properties,\n onAppend(name: string, value?: unknown) {\n name = name.trim();\n if (name.length === 0) return;\n\n engine.init([...field, name], value);\n },\n onDelete(name: string) {\n return engine.delete([...field, name]);\n },\n };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { objectGet, objectSet, stringifyFieldKey } from './utils';\nimport type { FieldKey } from './types';\nimport { Stf, useDataEngine } from './stf';\n\nexport type DefaultValue<T = unknown> = T | (() => T);\n\nfunction getDefaultValue<T>(defaultValue: DefaultValue<T>): T {\n return typeof defaultValue === 'function' ? (defaultValue as () => T)() : defaultValue;\n}\n\nexport interface DataEngineListener {\n /**\n * when specified, only call the listener for events affecting the specified field.\n */\n field?: FieldKey;\n /**\n * when field value is changed\n */\n onUpdate?: (key: FieldKey, ctx: OnUpdateContext) => void;\n /**\n * when `init(field)` is called\n */\n onInit?: (key: FieldKey) => void;\n /**\n * when `delete(field)` is called\n */\n onDelete?: (key: FieldKey) => void;\n}\n\ninterface OnUpdateContext {\n /**\n * An update is swallow if the change doesn't affect the values of children fields.\n */\n swallow: boolean;\n}\n\nclass ListenerManager {\n private readonly listeners = new Set<DataEngineListener>();\n private readonly indexed = new Map<string, Set<DataEngineListener>>();\n\n add(listener: DataEngineListener) {\n if (!listener.field) {\n this.listeners.add(listener);\n return;\n }\n const key = stringifyFieldKey(listener.field);\n const set = this.indexed.get(key) ?? new Set();\n set.add(listener);\n this.indexed.set(key, set);\n }\n\n remove(listener: DataEngineListener) {\n if (!listener.field) {\n this.listeners.delete(listener);\n } else {\n this.indexed.get(stringifyFieldKey(listener.field))?.delete(listener);\n }\n }\n\n onUpdate(field: FieldKey, ctx: OnUpdateContext) {\n for (const v of this.listeners) v.onUpdate?.(field, ctx);\n const updatedKey = stringifyFieldKey(field);\n\n if (ctx.swallow) {\n const set = this.indexed.get(updatedKey);\n if (set) for (const v of set) v.onUpdate?.(field, ctx);\n } else {\n for (const [k, listeners] of this.indexed.entries()) {\n if (k !== updatedKey && !k.startsWith(updatedKey + '.')) continue;\n for (const v of listeners) v.onUpdate?.(field, ctx);\n }\n }\n }\n\n onInit(field: FieldKey) {\n for (const v of this.listeners) v.onInit?.(field);\n const set = this.indexed.get(stringifyFieldKey(field));\n if (set) for (const v of set) v.onInit?.(field);\n }\n\n onDelete(field: FieldKey) {\n for (const v of this.listeners) v.onDelete?.(field);\n const set = this.indexed.get(stringifyFieldKey(field));\n if (set) for (const v of set) v.onDelete?.(field);\n }\n}\n\nexport class DataEngine {\n private data: NonNullable<object>;\n private attachedDataMap = new Map<string, unknown>();\n private readonly listeners = new ListenerManager();\n\n constructor(defaultValues: DefaultValue<NonNullable<object>> = {}) {\n this.data = getDefaultValue(defaultValues);\n }\n\n listen(listener: DataEngineListener) {\n this.listeners.add(listener);\n }\n\n unlisten(listener: DataEngineListener) {\n this.listeners.remove(listener);\n }\n\n getData() {\n return this.data;\n }\n\n /**\n * init a field\n * @param key the key of field\n * @param defaultValue the initial value, the field is also created for `undefined`\n * @returns the value of initialized field, or the current value of field if already initialized\n */\n init(key: FieldKey, defaultValue?: DefaultValue): unknown {\n if (key.length === 0) return this.data;\n let cur = this.data as Record<string, unknown>;\n const currentKey: FieldKey = [];\n\n for (let i = 0; i < key.length; i++) {\n const propKey = key[i];\n const propValue = cur[propKey];\n\n if (i === key.length - 1) {\n if (propValue !== undefined) return propValue;\n cur[propKey] = getDefaultValue(defaultValue);\n\n this.listeners.onUpdate(currentKey, { swallow: true });\n this.listeners.onInit(key);\n return cur[propKey];\n } else if (typeof propValue === 'object' && propValue !== null) {\n cur = propValue as Record<string, unknown>;\n } else {\n if (propValue !== undefined)\n console.warn(\n `the original value of field ${currentKey.join('.')} is overidden, this might be unexpected.`,\n );\n\n cur = cur[propKey] = {};\n this.listeners.onUpdate(currentKey, { swallow: true });\n }\n currentKey.push(propKey);\n }\n }\n\n delete(key: FieldKey): unknown | undefined {\n if (key.length === 0) return;\n const parentKey = key.slice(0, -1);\n const prop = key[key.length - 1];\n const parent = this.get(parentKey);\n\n if (Array.isArray(parent) && typeof prop === 'number') {\n const [deleted] = parent.splice(prop, 1);\n this.listeners.onUpdate(parentKey, { swallow: false });\n this.listeners.onDelete(key);\n return deleted;\n } else if (typeof parent === 'object' && parent !== null) {\n const temp = (parent as Record<string, unknown>)[prop];\n delete parent[prop as never];\n this.listeners.onUpdate(parentKey, { swallow: true });\n this.listeners.onDelete(key);\n return temp;\n }\n }\n\n get(key: FieldKey) {\n return objectGet(this.data, key);\n }\n\n /**\n * update the value of field if it exists\n * @returns if the field is updated\n */\n update(key: FieldKey, value: unknown): boolean {\n try {\n this.data = objectSet(this.data, key, value) as NonNullable<object>;\n this.listeners.onUpdate(key, { swallow: false });\n return true;\n } catch {\n return false;\n }\n }\n\n attachedData<T>(namespace: string) {\n return {\n get: (field: FieldKey): T | undefined => {\n return this.attachedDataMap.get(`${namespace}:${stringifyFieldKey(field)}`) as\n | T\n | undefined;\n },\n set: (field: FieldKey, value: T) => {\n this.attachedDataMap.set(`${namespace}:${stringifyFieldKey(field)}`, value);\n },\n delete: (field?: FieldKey) => {\n if (field) {\n this.attachedDataMap.delete(`${namespace}:${stringifyFieldKey(field)}`);\n } else {\n for (const key of this.attachedDataMap.keys()) {\n if (key.startsWith(`${namespace}:`)) this.attachedDataMap.delete(key);\n }\n }\n },\n };\n }\n\n reset(data: Record<string, unknown>) {\n this.update([], data);\n this.attachedDataMap.clear();\n }\n}\n\nexport function useFieldValue<V = unknown>(\n key: FieldKey,\n options: {\n stf?: Stf;\n defaultValue?: DefaultValue;\n\n /**\n * compute value from the actual field value.\n *\n * to re-compute on in-place updates (may happen on objects, arrays), you should clone the object here.\n */\n compute?: (currentValue: unknown) => V;\n /** determine whether the value/computed value is changed */\n isChanged?: (prev: V, next: V) => boolean;\n } = {},\n) {\n const engine = useDataEngine(options.stf);\n const { compute = (v) => v as V, defaultValue, isChanged = (a, b) => a !== b } = options;\n const [value, setValue] = useState<V>(() => compute(engine.init(key, defaultValue)));\n\n useListener({\n field: key,\n onUpdate() {\n const computed = compute(engine.get(key));\n if (isChanged(value, computed)) setValue(computed);\n },\n onDelete() {\n const computed = compute(undefined);\n if (isChanged(value, computed)) setValue(computed);\n },\n });\n\n return [value, (newValue: unknown) => engine.update(key, newValue)] as const;\n}\n\nexport function useListener(listener: DataEngineListener & { stf?: Stf }) {\n const engine = useDataEngine(listener.stf);\n const listenerRef = useRef(listener);\n listenerRef.current = listener;\n\n useEffect(() => {\n const internal: DataEngineListener = {\n field: listener.field,\n onDelete(...args) {\n return listenerRef.current.onDelete?.(...args);\n },\n onInit(...args) {\n return listenerRef.current.onInit?.(...args);\n },\n onUpdate(...args) {\n return listenerRef.current.onUpdate?.(...args);\n },\n };\n\n engine.listen(internal);\n return () => {\n engine.unlisten(internal);\n };\n }, [engine, listener.field]);\n}\n"],"mappings":"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"}
|
package/dist/lib/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","names":[],"sources":["../../src/lib/utils.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAUgB,iBAVA,eAAA,CAUS,CAAA,EAAA,OAAA,EAAA,EAAA,CAAA,EAAA,OAAA,EAAA,CAAA,EAAA,OAAA;AAiBT,iBAjBA,SAAA,CAiB6B,GAAQ,EAAA,OAAA,EAAA,GAAA,EAAA,CAAA,MAAA,GAAA,MAAA,CAAA,EAAA,CAAA,EAAA,OAAA,GAAA,SAAA;AAcrD;AAsCA;;;;iBApDgB,SAAA,oBAA6B;;;;iBAc7B,SAAA;iBAsCA,iBAAA,WAA4B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-PXiOOMNs.d.ts","names":[],"sources":["../src/lib/types.ts"],"sourcesContent":[],"mappings":";KAAY,QAAA"}
|
package/dist/utils-W82BoH4I.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
//#region src/lib/utils.ts
|
|
2
|
-
/**
|
|
3
|
-
* test if array a starts with array b, only compare values via `===`.
|
|
4
|
-
*/
|
|
5
|
-
function arrayStartsWith(a, b) {
|
|
6
|
-
if (b.length > a.length) return false;
|
|
7
|
-
for (let i = 0; i < b.length; i++) if (a[i] !== b[i]) return false;
|
|
8
|
-
return true;
|
|
9
|
-
}
|
|
10
|
-
function objectGet(obj, key) {
|
|
11
|
-
let cur = obj;
|
|
12
|
-
for (const prop of key) {
|
|
13
|
-
if (typeof cur !== "object" || cur === null || !(prop in cur)) return;
|
|
14
|
-
cur = cur[prop];
|
|
15
|
-
}
|
|
16
|
-
return cur;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* set the value of field if it exists (in place)
|
|
20
|
-
*
|
|
21
|
-
* @returns updated value, throw error if parent object doesn't exist
|
|
22
|
-
*/
|
|
23
|
-
function objectSet(obj, key, value) {
|
|
24
|
-
if (key.length === 0) return value;
|
|
25
|
-
const parent = objectGet(obj, key.slice(0, -1));
|
|
26
|
-
if (typeof parent !== "object" || parent === null) throw new Error("missing parent object");
|
|
27
|
-
parent[key[key.length - 1]] = value;
|
|
28
|
-
return obj;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* doesn't handle recursive objects
|
|
32
|
-
*/
|
|
33
|
-
function deepEqual(a, b) {
|
|
34
|
-
if (a === b) return true;
|
|
35
|
-
if (a == null || b == null) return false;
|
|
36
|
-
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
37
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
38
|
-
if (a.length !== b.length) return false;
|
|
39
|
-
return a.every((item, index) => deepEqual(item, b[index]));
|
|
40
|
-
}
|
|
41
|
-
if (Array.isArray(a) || Array.isArray(b)) return false;
|
|
42
|
-
const keysA = Object.keys(a);
|
|
43
|
-
const keysB = Object.keys(b);
|
|
44
|
-
if (keysA.length !== keysB.length) return false;
|
|
45
|
-
return keysA.every((key) => Object.prototype.hasOwnProperty.call(b, key) && deepEqual(a[key], b[key]));
|
|
46
|
-
}
|
|
47
|
-
function stringifyFieldKey(fieldKey) {
|
|
48
|
-
return fieldKey.map((v) => `${typeof v}:${v}`).join(".");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
//#endregion
|
|
52
|
-
export { stringifyFieldKey as a, objectSet as i, deepEqual as n, objectGet as r, arrayStartsWith as t };
|
|
53
|
-
//# sourceMappingURL=utils-W82BoH4I.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils-W82BoH4I.js","names":[],"sources":["../src/lib/utils.ts"],"sourcesContent":["import { FieldKey } from './types';\n\n/**\n * test if array a starts with array b, only compare values via `===`.\n */\nexport function arrayStartsWith(a: unknown[], b: unknown[]): boolean {\n if (b.length > a.length) return false;\n\n for (let i = 0; i < b.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n\n return true;\n}\n\nexport function objectGet(obj: unknown, key: (string | number)[]): unknown | undefined {\n let cur = obj;\n\n for (const prop of key) {\n if (typeof cur !== 'object' || cur === null || !(prop in cur)) return;\n\n cur = cur[prop as keyof typeof cur];\n }\n\n return cur;\n}\n\n/**\n * set the value of field if it exists (in place)\n *\n * @returns updated value, throw error if parent object doesn't exist\n */\nexport function objectSet(obj: unknown, key: FieldKey, value: unknown): unknown {\n if (key.length === 0) {\n return value;\n }\n\n const parent = objectGet(obj, key.slice(0, -1));\n if (typeof parent !== 'object' || parent === null) throw new Error('missing parent object');\n (parent as Record<string, unknown>)[key[key.length - 1]] = value;\n return obj;\n}\n\n/**\n * doesn't handle recursive objects\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) {\n return true;\n }\n\n if (a == null || b == null) {\n return false;\n }\n\n if (typeof a !== 'object' || typeof b !== 'object') {\n return false;\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return false;\n }\n return a.every((item, index) => deepEqual(item, b[index]));\n }\n\n if (Array.isArray(a) || Array.isArray(b)) {\n return false;\n }\n\n const keysA = Object.keys(a as object);\n const keysB = Object.keys(b as object);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n return keysA.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(b, key) &&\n deepEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]),\n );\n}\n\nexport function stringifyFieldKey(fieldKey: FieldKey) {\n return fieldKey.map((v) => `${typeof v}:${v}`).join('.');\n}\n"],"mappings":";;;;AAKA,SAAgB,gBAAgB,GAAc,GAAuB;AACnE,KAAI,EAAE,SAAS,EAAE,OAAQ,QAAO;AAEhC,MAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,IAC5B,KAAI,EAAE,OAAO,EAAE,GAAI,QAAO;AAG5B,QAAO;;AAGT,SAAgB,UAAU,KAAc,KAA+C;CACrF,IAAI,MAAM;AAEV,MAAK,MAAM,QAAQ,KAAK;AACtB,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,QAAQ,KAAM;AAE/D,QAAM,IAAI;;AAGZ,QAAO;;;;;;;AAQT,SAAgB,UAAU,KAAc,KAAe,OAAyB;AAC9E,KAAI,IAAI,WAAW,EACjB,QAAO;CAGT,MAAM,SAAS,UAAU,KAAK,IAAI,MAAM,GAAG,GAAG,CAAC;AAC/C,KAAI,OAAO,WAAW,YAAY,WAAW,KAAM,OAAM,IAAI,MAAM,wBAAwB;AAC3F,CAAC,OAAmC,IAAI,IAAI,SAAS,MAAM;AAC3D,QAAO;;;;;AAMT,SAAgB,UAAU,GAAY,GAAqB;AACzD,KAAI,MAAM,EACR,QAAO;AAGT,KAAI,KAAK,QAAQ,KAAK,KACpB,QAAO;AAGT,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SACxC,QAAO;AAGT,KAAI,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,EAAE,EAAE;AACxC,MAAI,EAAE,WAAW,EAAE,OACjB,QAAO;AAET,SAAO,EAAE,OAAO,MAAM,UAAU,UAAU,MAAM,EAAE,OAAO,CAAC;;AAG5D,KAAI,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,EAAE,CACtC,QAAO;CAGT,MAAM,QAAQ,OAAO,KAAK,EAAY;CACtC,MAAM,QAAQ,OAAO,KAAK,EAAY;AAEtC,KAAI,MAAM,WAAW,MAAM,OACzB,QAAO;AAGT,QAAO,MAAM,OACV,QACC,OAAO,UAAU,eAAe,KAAK,GAAG,IAAI,IAC5C,UAAW,EAA8B,MAAO,EAA8B,KAAK,CACtF;;AAGH,SAAgB,kBAAkB,UAAoB;AACpD,QAAO,SAAS,KAAK,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,IAAI"}
|