@stonecrop/stonecrop 0.4.37 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -3
- package/dist/src/composable.d.ts +74 -8
- package/dist/src/composable.d.ts.map +1 -1
- package/dist/src/composable.js +348 -0
- package/dist/src/composables/operation-log.d.ts +136 -0
- package/dist/src/composables/operation-log.d.ts.map +1 -0
- package/dist/src/composables/operation-log.js +221 -0
- package/dist/src/doctype.d.ts +9 -1
- package/dist/src/doctype.d.ts.map +1 -1
- package/dist/{doctype.js → src/doctype.js} +9 -3
- package/dist/src/field-triggers.d.ts +178 -0
- package/dist/src/field-triggers.d.ts.map +1 -0
- package/dist/src/field-triggers.js +564 -0
- package/dist/src/index.d.ts +12 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +18 -0
- package/dist/src/plugins/index.d.ts +11 -13
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/plugins/index.js +90 -0
- package/dist/src/registry.d.ts +9 -3
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/{registry.js → src/registry.js} +14 -1
- package/dist/src/stonecrop.d.ts +350 -114
- package/dist/src/stonecrop.d.ts.map +1 -1
- package/dist/src/stonecrop.js +251 -0
- package/dist/src/stores/hst.d.ts +157 -0
- package/dist/src/stores/hst.d.ts.map +1 -0
- package/dist/src/stores/hst.js +483 -0
- package/dist/src/stores/index.d.ts +5 -1
- package/dist/src/stores/index.d.ts.map +1 -1
- package/dist/{stores → src/stores}/index.js +4 -1
- package/dist/src/stores/operation-log.d.ts +268 -0
- package/dist/src/stores/operation-log.d.ts.map +1 -0
- package/dist/src/stores/operation-log.js +571 -0
- package/dist/src/types/field-triggers.d.ts +186 -0
- package/dist/src/types/field-triggers.d.ts.map +1 -0
- package/dist/src/types/field-triggers.js +4 -0
- package/dist/src/types/index.d.ts +13 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +4 -0
- package/dist/src/types/operation-log.d.ts +165 -0
- package/dist/src/types/operation-log.d.ts.map +1 -0
- package/dist/src/types/registry.d.ts +11 -0
- package/dist/src/types/registry.d.ts.map +1 -0
- package/dist/src/types/registry.js +0 -0
- package/dist/stonecrop.d.ts +1555 -159
- package/dist/stonecrop.js +1974 -7028
- package/dist/stonecrop.js.map +1 -1
- package/dist/stonecrop.umd.cjs +4 -8
- package/dist/stonecrop.umd.cjs.map +1 -1
- package/dist/tests/setup.d.ts +5 -0
- package/dist/tests/setup.d.ts.map +1 -0
- package/dist/tests/setup.js +15 -0
- package/package.json +5 -4
- package/src/composable.ts +481 -31
- package/src/composables/operation-log.ts +254 -0
- package/src/doctype.ts +9 -3
- package/src/field-triggers.ts +671 -0
- package/src/index.ts +50 -4
- package/src/plugins/index.ts +70 -22
- package/src/registry.ts +18 -3
- package/src/stonecrop.ts +246 -155
- package/src/stores/hst.ts +703 -0
- package/src/stores/index.ts +6 -1
- package/src/stores/operation-log.ts +671 -0
- package/src/types/field-triggers.ts +201 -0
- package/src/types/index.ts +17 -6
- package/src/types/operation-log.ts +205 -0
- package/src/types/registry.ts +10 -0
- package/dist/composable.js +0 -50
- package/dist/index.js +0 -6
- package/dist/plugins/index.js +0 -49
- package/dist/src/stores/data.d.ts +0 -11
- package/dist/src/stores/data.d.ts.map +0 -1
- package/dist/stores/data.js +0 -7
- package/src/stores/data.ts +0 -8
- /package/dist/{exceptions.js → src/exceptions.js} +0 -0
- /package/dist/{types/index.js → src/types/operation-log.js} +0 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { getGlobalTriggerEngine } from '../field-triggers';
|
|
2
|
+
import { useOperationLogStore } from './operation-log';
|
|
3
|
+
/**
|
|
4
|
+
* Get the operation log store if available
|
|
5
|
+
*/
|
|
6
|
+
function getOperationLogStore() {
|
|
7
|
+
try {
|
|
8
|
+
return useOperationLogStore();
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// Operation log is optional
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Global HST Manager (Singleton)
|
|
17
|
+
* Manages hierarchical state trees and provides access to the global registry.
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
class HST {
|
|
22
|
+
static instance;
|
|
23
|
+
/**
|
|
24
|
+
* Gets the singleton instance of HST
|
|
25
|
+
* @returns The HST singleton instance
|
|
26
|
+
*/
|
|
27
|
+
static getInstance() {
|
|
28
|
+
if (!HST.instance) {
|
|
29
|
+
HST.instance = new HST();
|
|
30
|
+
}
|
|
31
|
+
return HST.instance;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Gets the global registry instance
|
|
35
|
+
* @returns The global registry object or undefined if not found
|
|
36
|
+
*/
|
|
37
|
+
getRegistry() {
|
|
38
|
+
// In test environment, try different ways to access Registry
|
|
39
|
+
// First, try the global Registry if it exists
|
|
40
|
+
if (typeof globalThis !== 'undefined') {
|
|
41
|
+
const globalRegistry = globalThis.Registry?._root;
|
|
42
|
+
if (globalRegistry) {
|
|
43
|
+
return globalRegistry;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Try to access through window (browser environment)
|
|
47
|
+
if (typeof window !== 'undefined') {
|
|
48
|
+
const windowRegistry = window.Registry?._root;
|
|
49
|
+
if (windowRegistry) {
|
|
50
|
+
return windowRegistry;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Try to access through global (Node environment)
|
|
54
|
+
if (typeof global !== 'undefined' && global) {
|
|
55
|
+
const nodeRegistry = global.Registry?._root;
|
|
56
|
+
if (nodeRegistry) {
|
|
57
|
+
return nodeRegistry;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// If we can't find it globally, it might not be set up
|
|
61
|
+
// This is expected in test environments where Registry is created locally
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Helper method to get doctype metadata from the registry
|
|
66
|
+
* @param doctype - The name of the doctype to retrieve metadata for
|
|
67
|
+
* @returns The doctype metadata object or undefined if not found
|
|
68
|
+
*/
|
|
69
|
+
getDoctypeMeta(doctype) {
|
|
70
|
+
const registry = this.getRegistry();
|
|
71
|
+
if (registry && typeof registry === 'object' && 'registry' in registry) {
|
|
72
|
+
return registry.registry[doctype];
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Enhanced HST Proxy with tree navigation
|
|
78
|
+
class HSTProxy {
|
|
79
|
+
target;
|
|
80
|
+
parentPath;
|
|
81
|
+
rootNode;
|
|
82
|
+
doctype;
|
|
83
|
+
parentDoctype;
|
|
84
|
+
hst;
|
|
85
|
+
constructor(target, doctype, parentPath = '', rootNode = null, parentDoctype) {
|
|
86
|
+
this.target = target;
|
|
87
|
+
this.parentPath = parentPath;
|
|
88
|
+
this.rootNode = rootNode || this;
|
|
89
|
+
this.doctype = doctype;
|
|
90
|
+
this.parentDoctype = parentDoctype;
|
|
91
|
+
this.hst = HST.getInstance();
|
|
92
|
+
return new Proxy(this, {
|
|
93
|
+
get(hst, prop) {
|
|
94
|
+
// Return HST methods directly
|
|
95
|
+
if (prop in hst)
|
|
96
|
+
return hst[prop];
|
|
97
|
+
// Handle property access - return tree nodes for navigation
|
|
98
|
+
const path = String(prop);
|
|
99
|
+
return hst.getNode(path);
|
|
100
|
+
},
|
|
101
|
+
set(hst, prop, value) {
|
|
102
|
+
const path = String(prop);
|
|
103
|
+
hst.set(path, value);
|
|
104
|
+
return true;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
get(path) {
|
|
109
|
+
return this.resolveValue(path);
|
|
110
|
+
}
|
|
111
|
+
// Method to get a tree-wrapped node for navigation
|
|
112
|
+
getNode(path) {
|
|
113
|
+
const fullPath = this.resolvePath(path);
|
|
114
|
+
const value = this.resolveValue(path);
|
|
115
|
+
// Determine the correct doctype for this node based on the path
|
|
116
|
+
const pathSegments = fullPath.split('.');
|
|
117
|
+
let nodeDoctype = this.doctype;
|
|
118
|
+
// If we're at the root level and this is a StonecropStore, use the first path segment as the doctype
|
|
119
|
+
if (this.doctype === 'StonecropStore' && pathSegments.length >= 1) {
|
|
120
|
+
nodeDoctype = pathSegments[0];
|
|
121
|
+
}
|
|
122
|
+
// Always wrap in HSTProxy for tree navigation
|
|
123
|
+
if (typeof value === 'object' && value !== null && !this.isPrimitive(value)) {
|
|
124
|
+
return new HSTProxy(value, nodeDoctype, fullPath, this.rootNode, this.parentDoctype);
|
|
125
|
+
}
|
|
126
|
+
// For primitives, return a minimal wrapper that throws on tree operations
|
|
127
|
+
return new HSTProxy(value, nodeDoctype, fullPath, this.rootNode, this.parentDoctype);
|
|
128
|
+
}
|
|
129
|
+
set(path, value, source = 'user') {
|
|
130
|
+
// Get current value for change context
|
|
131
|
+
const fullPath = this.resolvePath(path);
|
|
132
|
+
const beforeValue = this.has(path) ? this.get(path) : undefined;
|
|
133
|
+
// Log operation if not from undo/redo and store is available
|
|
134
|
+
if (source !== 'undo' && source !== 'redo') {
|
|
135
|
+
const logStore = getOperationLogStore();
|
|
136
|
+
if (logStore && typeof logStore.addOperation === 'function') {
|
|
137
|
+
const pathSegments = fullPath.split('.');
|
|
138
|
+
const doctype = this.doctype === 'StonecropStore' && pathSegments.length >= 1 ? pathSegments[0] : this.doctype;
|
|
139
|
+
const recordId = pathSegments.length >= 2 ? pathSegments[1] : undefined;
|
|
140
|
+
const fieldname = pathSegments.slice(2).join('.') || pathSegments[pathSegments.length - 1];
|
|
141
|
+
// Detect if this is a DELETE operation (setting to undefined when a value existed)
|
|
142
|
+
const isDelete = value === undefined && beforeValue !== undefined;
|
|
143
|
+
const operationType = isDelete ? 'delete' : 'set';
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
145
|
+
logStore.addOperation({
|
|
146
|
+
type: operationType,
|
|
147
|
+
path: fullPath,
|
|
148
|
+
fieldname,
|
|
149
|
+
beforeValue,
|
|
150
|
+
afterValue: value,
|
|
151
|
+
doctype,
|
|
152
|
+
recordId,
|
|
153
|
+
reversible: true, // Default to reversible, can be changed by field triggers
|
|
154
|
+
}, source);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Update the value
|
|
158
|
+
this.updateValue(path, value);
|
|
159
|
+
// Trigger field actions asynchronously (don't block the set operation)
|
|
160
|
+
void this.triggerFieldActions(fullPath, beforeValue, value);
|
|
161
|
+
}
|
|
162
|
+
has(path) {
|
|
163
|
+
try {
|
|
164
|
+
// Handle empty path case
|
|
165
|
+
if (path === '') {
|
|
166
|
+
return true; // empty path refers to the root object
|
|
167
|
+
}
|
|
168
|
+
const segments = this.parsePath(path);
|
|
169
|
+
let current = this.target;
|
|
170
|
+
for (let i = 0; i < segments.length; i++) {
|
|
171
|
+
const segment = segments[i];
|
|
172
|
+
if (current === null || current === undefined) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
// Check if this is the last segment
|
|
176
|
+
if (i === segments.length - 1) {
|
|
177
|
+
// For the final property, check if it exists
|
|
178
|
+
if (this.isImmutable(current)) {
|
|
179
|
+
return current.has(segment);
|
|
180
|
+
}
|
|
181
|
+
else if (this.isPiniaStore(current)) {
|
|
182
|
+
return (current.$state && segment in current.$state) || segment in current;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
return segment in current;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Navigate to the next level
|
|
189
|
+
current = this.getProperty(current, segment);
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Tree navigation methods
|
|
198
|
+
getParent() {
|
|
199
|
+
if (!this.parentPath)
|
|
200
|
+
return null;
|
|
201
|
+
const parentSegments = this.parentPath.split('.').slice(0, -1);
|
|
202
|
+
const parentPath = parentSegments.join('.');
|
|
203
|
+
if (parentPath === '') {
|
|
204
|
+
return this.rootNode;
|
|
205
|
+
}
|
|
206
|
+
// Return a wrapped node, not raw data
|
|
207
|
+
return this.rootNode.getNode(parentPath);
|
|
208
|
+
}
|
|
209
|
+
getRoot() {
|
|
210
|
+
return this.rootNode;
|
|
211
|
+
}
|
|
212
|
+
getPath() {
|
|
213
|
+
return this.parentPath;
|
|
214
|
+
}
|
|
215
|
+
getDepth() {
|
|
216
|
+
return this.parentPath ? this.parentPath.split('.').length : 0;
|
|
217
|
+
}
|
|
218
|
+
getBreadcrumbs() {
|
|
219
|
+
return this.parentPath ? this.parentPath.split('.') : [];
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Trigger an XState transition with optional context data
|
|
223
|
+
*/
|
|
224
|
+
async triggerTransition(transition, context) {
|
|
225
|
+
const triggerEngine = getGlobalTriggerEngine();
|
|
226
|
+
// Determine doctype and recordId from the current path
|
|
227
|
+
const pathSegments = this.parentPath.split('.');
|
|
228
|
+
let doctype = this.doctype;
|
|
229
|
+
let recordId;
|
|
230
|
+
// If we're at the root level and this is a StonecropStore, use the first path segment as the doctype
|
|
231
|
+
if (this.doctype === 'StonecropStore' && pathSegments.length >= 1) {
|
|
232
|
+
doctype = pathSegments[0];
|
|
233
|
+
}
|
|
234
|
+
// Extract recordId from path if it follows the expected pattern
|
|
235
|
+
if (pathSegments.length >= 2) {
|
|
236
|
+
recordId = pathSegments[1];
|
|
237
|
+
}
|
|
238
|
+
// Build transition context
|
|
239
|
+
const transitionContext = {
|
|
240
|
+
path: this.parentPath,
|
|
241
|
+
fieldname: '', // No specific field for transitions
|
|
242
|
+
beforeValue: undefined,
|
|
243
|
+
afterValue: undefined,
|
|
244
|
+
operation: 'set',
|
|
245
|
+
doctype,
|
|
246
|
+
recordId,
|
|
247
|
+
timestamp: new Date(),
|
|
248
|
+
store: this.rootNode || undefined,
|
|
249
|
+
transition,
|
|
250
|
+
currentState: context?.currentState,
|
|
251
|
+
targetState: context?.targetState,
|
|
252
|
+
fsmContext: context?.fsmContext,
|
|
253
|
+
};
|
|
254
|
+
// Log FSM transition operation
|
|
255
|
+
const logStore = getOperationLogStore();
|
|
256
|
+
if (logStore && typeof logStore.addOperation === 'function') {
|
|
257
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
258
|
+
logStore.addOperation({
|
|
259
|
+
type: 'transition',
|
|
260
|
+
path: this.parentPath,
|
|
261
|
+
fieldname: transition,
|
|
262
|
+
beforeValue: context?.currentState,
|
|
263
|
+
afterValue: context?.targetState,
|
|
264
|
+
doctype,
|
|
265
|
+
recordId,
|
|
266
|
+
reversible: false, // FSM transitions are generally not reversible
|
|
267
|
+
metadata: {
|
|
268
|
+
transition,
|
|
269
|
+
currentState: context?.currentState,
|
|
270
|
+
targetState: context?.targetState,
|
|
271
|
+
fsmContext: context?.fsmContext,
|
|
272
|
+
},
|
|
273
|
+
}, 'user');
|
|
274
|
+
}
|
|
275
|
+
// Execute transition actions
|
|
276
|
+
return await triggerEngine.executeTransitionActions(transitionContext);
|
|
277
|
+
}
|
|
278
|
+
// Private helper methods
|
|
279
|
+
resolvePath(path) {
|
|
280
|
+
if (path === '')
|
|
281
|
+
return this.parentPath;
|
|
282
|
+
return this.parentPath ? `${this.parentPath}.${path}` : path;
|
|
283
|
+
}
|
|
284
|
+
resolveValue(path) {
|
|
285
|
+
// Handle empty path - return the target object
|
|
286
|
+
if (path === '') {
|
|
287
|
+
return this.target;
|
|
288
|
+
}
|
|
289
|
+
const segments = this.parsePath(path);
|
|
290
|
+
let current = this.target;
|
|
291
|
+
for (const segment of segments) {
|
|
292
|
+
if (current === null || current === undefined) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
current = this.getProperty(current, segment);
|
|
296
|
+
}
|
|
297
|
+
return current;
|
|
298
|
+
}
|
|
299
|
+
updateValue(path, value) {
|
|
300
|
+
// Handle empty path case - should throw error
|
|
301
|
+
if (path === '') {
|
|
302
|
+
throw new Error('Cannot set value on empty path');
|
|
303
|
+
}
|
|
304
|
+
const segments = this.parsePath(path);
|
|
305
|
+
const lastSegment = segments.pop();
|
|
306
|
+
let current = this.target;
|
|
307
|
+
// Navigate to parent object
|
|
308
|
+
for (const segment of segments) {
|
|
309
|
+
current = this.getProperty(current, segment);
|
|
310
|
+
if (current === null || current === undefined) {
|
|
311
|
+
throw new Error(`Cannot set property on null/undefined path: ${path}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// Set the final property
|
|
315
|
+
this.setProperty(current, lastSegment, value);
|
|
316
|
+
}
|
|
317
|
+
getProperty(obj, key) {
|
|
318
|
+
// Immutable objects
|
|
319
|
+
if (this.isImmutable(obj)) {
|
|
320
|
+
return obj.get(key);
|
|
321
|
+
}
|
|
322
|
+
// Vue reactive object
|
|
323
|
+
if (this.isVueReactive(obj)) {
|
|
324
|
+
return obj[key];
|
|
325
|
+
}
|
|
326
|
+
// Pinia store
|
|
327
|
+
if (this.isPiniaStore(obj)) {
|
|
328
|
+
return obj.$state?.[key] ?? obj[key];
|
|
329
|
+
}
|
|
330
|
+
// Plain object
|
|
331
|
+
return obj[key];
|
|
332
|
+
}
|
|
333
|
+
setProperty(obj, key, value) {
|
|
334
|
+
// Immutable objects
|
|
335
|
+
if (this.isImmutable(obj)) {
|
|
336
|
+
throw new Error('Cannot directly mutate immutable objects. Use immutable update methods instead.');
|
|
337
|
+
}
|
|
338
|
+
// Pinia store
|
|
339
|
+
if (this.isPiniaStore(obj)) {
|
|
340
|
+
if (obj.$patch) {
|
|
341
|
+
obj.$patch({ [key]: value });
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
;
|
|
345
|
+
obj[key] = value;
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// Vue reactive or plain object
|
|
350
|
+
;
|
|
351
|
+
obj[key] = value;
|
|
352
|
+
}
|
|
353
|
+
async triggerFieldActions(fullPath, beforeValue, afterValue) {
|
|
354
|
+
try {
|
|
355
|
+
// Guard against undefined or null fullPath
|
|
356
|
+
if (!fullPath || typeof fullPath !== 'string') {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const pathSegments = fullPath.split('.');
|
|
360
|
+
// Only trigger field actions for actual field changes (at least 3 levels deep: doctype.recordId.fieldname)
|
|
361
|
+
// Skip triggering for doctype-level or record-level changes
|
|
362
|
+
if (pathSegments.length < 3) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const triggerEngine = getGlobalTriggerEngine();
|
|
366
|
+
const fieldname = pathSegments.slice(2).join('.') || pathSegments[pathSegments.length - 1];
|
|
367
|
+
// Determine the correct doctype for this path using the same logic as getNode()
|
|
368
|
+
// The path should be in format: "doctype.recordId.fieldname"
|
|
369
|
+
let doctype = this.doctype;
|
|
370
|
+
// If we're at the root level and this is a StonecropStore, use the first path segment as the doctype
|
|
371
|
+
if (this.doctype === 'StonecropStore' && pathSegments.length >= 1) {
|
|
372
|
+
doctype = pathSegments[0];
|
|
373
|
+
}
|
|
374
|
+
let recordId;
|
|
375
|
+
// Extract recordId from path if it follows the expected pattern
|
|
376
|
+
if (pathSegments.length >= 2) {
|
|
377
|
+
recordId = pathSegments[1];
|
|
378
|
+
}
|
|
379
|
+
const context = {
|
|
380
|
+
path: fullPath,
|
|
381
|
+
fieldname,
|
|
382
|
+
beforeValue,
|
|
383
|
+
afterValue,
|
|
384
|
+
operation: 'set',
|
|
385
|
+
doctype,
|
|
386
|
+
recordId,
|
|
387
|
+
timestamp: new Date(),
|
|
388
|
+
store: this.rootNode || undefined, // Pass the root store for snapshot/rollback capabilities
|
|
389
|
+
};
|
|
390
|
+
await triggerEngine.executeFieldTriggers(context);
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
// Silently handle trigger errors to not break the main flow
|
|
394
|
+
// In production, you might want to log this error
|
|
395
|
+
if (error instanceof Error) {
|
|
396
|
+
// eslint-disable-next-line no-console
|
|
397
|
+
console.warn('Field trigger error:', error.message);
|
|
398
|
+
// Optional: emit an event or call error handler
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
isVueReactive(obj) {
|
|
403
|
+
return (obj &&
|
|
404
|
+
typeof obj === 'object' &&
|
|
405
|
+
'__v_isReactive' in obj &&
|
|
406
|
+
obj.__v_isReactive === true);
|
|
407
|
+
}
|
|
408
|
+
isPiniaStore(obj) {
|
|
409
|
+
return obj && typeof obj === 'object' && ('$state' in obj || '$patch' in obj || '$id' in obj);
|
|
410
|
+
}
|
|
411
|
+
isImmutable(obj) {
|
|
412
|
+
if (!obj || typeof obj !== 'object') {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
const hasGetMethod = 'get' in obj && typeof obj.get === 'function';
|
|
416
|
+
const hasSetMethod = 'set' in obj && typeof obj.set === 'function';
|
|
417
|
+
const hasHasMethod = 'has' in obj && typeof obj.has === 'function';
|
|
418
|
+
const hasImmutableMarkers = '__ownerID' in obj ||
|
|
419
|
+
'_map' in obj ||
|
|
420
|
+
'_list' in obj ||
|
|
421
|
+
'_origin' in obj ||
|
|
422
|
+
'_capacity' in obj ||
|
|
423
|
+
'_defaultValues' in obj ||
|
|
424
|
+
'_tail' in obj ||
|
|
425
|
+
'_root' in obj ||
|
|
426
|
+
('size' in obj && hasGetMethod && hasSetMethod);
|
|
427
|
+
let constructorName;
|
|
428
|
+
try {
|
|
429
|
+
const objWithConstructor = obj;
|
|
430
|
+
if ('constructor' in objWithConstructor &&
|
|
431
|
+
objWithConstructor.constructor &&
|
|
432
|
+
typeof objWithConstructor.constructor === 'object' &&
|
|
433
|
+
'name' in objWithConstructor.constructor) {
|
|
434
|
+
const nameValue = objWithConstructor.constructor.name;
|
|
435
|
+
constructorName = typeof nameValue === 'string' ? nameValue : undefined;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
constructorName = undefined;
|
|
440
|
+
}
|
|
441
|
+
const isImmutableConstructor = constructorName &&
|
|
442
|
+
(constructorName.includes('Map') ||
|
|
443
|
+
constructorName.includes('List') ||
|
|
444
|
+
constructorName.includes('Set') ||
|
|
445
|
+
constructorName.includes('Stack') ||
|
|
446
|
+
constructorName.includes('Seq')) &&
|
|
447
|
+
(hasGetMethod || hasSetMethod);
|
|
448
|
+
return Boolean((hasGetMethod && hasSetMethod && hasHasMethod && hasImmutableMarkers) ||
|
|
449
|
+
(hasGetMethod && hasSetMethod && isImmutableConstructor));
|
|
450
|
+
}
|
|
451
|
+
isPrimitive(value) {
|
|
452
|
+
// Don't wrap primitive values, functions, or null/undefined
|
|
453
|
+
return (value === null ||
|
|
454
|
+
value === undefined ||
|
|
455
|
+
typeof value === 'string' ||
|
|
456
|
+
typeof value === 'number' ||
|
|
457
|
+
typeof value === 'boolean' ||
|
|
458
|
+
typeof value === 'function' ||
|
|
459
|
+
typeof value === 'symbol' ||
|
|
460
|
+
typeof value === 'bigint');
|
|
461
|
+
}
|
|
462
|
+
parsePath(path) {
|
|
463
|
+
if (!path)
|
|
464
|
+
return [];
|
|
465
|
+
return path.split('.').filter(segment => segment.length > 0);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Factory function for HST creation
|
|
470
|
+
* Creates a new HSTNode proxy for hierarchical state tree navigation.
|
|
471
|
+
*
|
|
472
|
+
* @param target - The target object to wrap with HST functionality
|
|
473
|
+
* @param doctype - The document type identifier
|
|
474
|
+
* @param parentDoctype - Optional parent document type identifier
|
|
475
|
+
* @returns A new HSTNode proxy instance
|
|
476
|
+
*
|
|
477
|
+
* @public
|
|
478
|
+
*/
|
|
479
|
+
function createHST(target, doctype, parentDoctype) {
|
|
480
|
+
return new HSTProxy(target, doctype, '', null, parentDoctype);
|
|
481
|
+
}
|
|
482
|
+
// Export everything
|
|
483
|
+
export { HSTProxy, HST, createHST };
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { HST } from './hst';
|
|
2
|
+
declare const hst: HST;
|
|
1
3
|
declare const pinia: import("pinia").Pinia;
|
|
2
|
-
export { pinia };
|
|
4
|
+
export { hst, pinia };
|
|
5
|
+
export { useOperationLogStore } from './operation-log';
|
|
6
|
+
export type { HSTNode } from './hst';
|
|
3
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/stores/index.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,KAAK,uBAAgB,CAAA;AAU3B,OAAO,EAAE,KAAK,EAAE,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/stores/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAE3B,QAAA,MAAM,GAAG,KAAoB,CAAA;AAC7B,QAAA,MAAM,KAAK,uBAAgB,CAAA;AAU3B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AACrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACtD,YAAY,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { createPinia } from 'pinia';
|
|
2
2
|
import { PiniaSharedState } from 'pinia-shared-state';
|
|
3
|
+
import { HST } from './hst';
|
|
4
|
+
const hst = HST.getInstance();
|
|
3
5
|
const pinia = createPinia();
|
|
4
6
|
// Pass the plugin to your application's pinia plugin
|
|
5
7
|
pinia.use(PiniaSharedState({
|
|
6
8
|
enable: true,
|
|
7
9
|
initialize: true,
|
|
8
10
|
}));
|
|
9
|
-
export { pinia };
|
|
11
|
+
export { hst, pinia };
|
|
12
|
+
export { useOperationLogStore } from './operation-log';
|