@stonecrop/stonecrop 0.12.8 → 0.13.1

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 (60) hide show
  1. package/README.md +0 -1
  2. package/dist/composable.js +1 -0
  3. package/dist/composables/lazy-link.js +125 -0
  4. package/dist/composables/operation-log.js +224 -0
  5. package/dist/composables/stonecrop.js +504 -0
  6. package/dist/composables/use-lazy-link-state.js +125 -0
  7. package/dist/composables/use-stonecrop.js +476 -0
  8. package/dist/doctype.js +242 -0
  9. package/dist/exceptions.js +16 -0
  10. package/dist/field-triggers.js +575 -0
  11. package/dist/index.js +27 -0
  12. package/dist/operation-log-DB-dGNT9.js +593 -0
  13. package/dist/operation-log-DB-dGNT9.js.map +1 -0
  14. package/dist/plugins/index.js +99 -0
  15. package/dist/registry.js +423 -0
  16. package/dist/schema-validator.js +407 -0
  17. package/dist/src/composable.d.ts +11 -0
  18. package/dist/src/composable.d.ts.map +1 -0
  19. package/dist/src/composable.js +477 -0
  20. package/dist/src/composables/use-lazy-link-state.d.ts +25 -0
  21. package/dist/src/composables/use-lazy-link-state.d.ts.map +1 -0
  22. package/dist/src/composables/use-stonecrop.d.ts +93 -0
  23. package/dist/src/composables/use-stonecrop.d.ts.map +1 -0
  24. package/dist/src/composables/useNestedSchema.d.ts +110 -0
  25. package/dist/src/composables/useNestedSchema.d.ts.map +1 -0
  26. package/dist/src/composables/useNestedSchema.js +155 -0
  27. package/dist/src/stores/data.d.ts +11 -0
  28. package/dist/src/stores/data.d.ts.map +1 -0
  29. package/dist/src/stores/xstate.d.ts +31 -0
  30. package/dist/src/stores/xstate.d.ts.map +1 -0
  31. package/dist/src/tsdoc-metadata.json +11 -0
  32. package/dist/src/types/doctype.d.ts +0 -2
  33. package/dist/src/types/doctype.d.ts.map +1 -1
  34. package/dist/src/utils.d.ts +24 -0
  35. package/dist/src/utils.d.ts.map +1 -0
  36. package/dist/stonecrop.css +1 -0
  37. package/dist/stonecrop.d.ts +0 -2
  38. package/dist/stonecrop.umd.cjs +6 -0
  39. package/dist/stonecrop.umd.cjs.map +1 -0
  40. package/dist/stores/data.js +7 -0
  41. package/dist/stores/hst.js +496 -0
  42. package/dist/stores/index.js +12 -0
  43. package/dist/stores/operation-log.js +580 -0
  44. package/dist/stores/xstate.js +29 -0
  45. package/dist/tests/setup.d.ts +5 -0
  46. package/dist/tests/setup.d.ts.map +1 -0
  47. package/dist/tests/setup.js +15 -0
  48. package/dist/types/composable.js +0 -0
  49. package/dist/types/doctype.js +0 -0
  50. package/dist/types/field-triggers.js +4 -0
  51. package/dist/types/hst.js +0 -0
  52. package/dist/types/index.js +10 -0
  53. package/dist/types/operation-log.js +0 -0
  54. package/dist/types/plugin.js +0 -0
  55. package/dist/types/registry.js +0 -0
  56. package/dist/types/schema-validator.js +13 -0
  57. package/dist/types/stonecrop.js +0 -0
  58. package/dist/utils.js +46 -0
  59. package/package.json +4 -4
  60. package/src/types/doctype.ts +0 -2
@@ -0,0 +1,580 @@
1
+ import { defineStore } from 'pinia';
2
+ import { ref, computed, watch } from 'vue';
3
+ import { useLocalStorage } from '@vueuse/core';
4
+ /**
5
+ * Generate a UUID using crypto API or fallback
6
+ */
7
+ function generateId() {
8
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
9
+ return crypto.randomUUID();
10
+ }
11
+ // Fallback for environments without crypto.randomUUID
12
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
13
+ }
14
+ function serializeForBroadcast(message) {
15
+ const serialized = {
16
+ type: message.type,
17
+ clientId: message.clientId,
18
+ timestamp: message.timestamp.toISOString(),
19
+ };
20
+ if (message.operation) {
21
+ serialized.operation = {
22
+ ...message.operation,
23
+ timestamp: message.operation.timestamp.toISOString(),
24
+ };
25
+ }
26
+ if (message.operations) {
27
+ serialized.operations = message.operations.map(op => ({
28
+ ...op,
29
+ timestamp: op.timestamp.toISOString(),
30
+ }));
31
+ }
32
+ return serialized;
33
+ }
34
+ /**
35
+ * Deserialize a message from BroadcastChannel
36
+ * Converts ISO strings back to Date objects
37
+ */
38
+ function deserializeFromBroadcast(serialized) {
39
+ const message = {
40
+ type: serialized.type,
41
+ clientId: serialized.clientId,
42
+ timestamp: new Date(serialized.timestamp),
43
+ };
44
+ if (serialized.operation) {
45
+ message.operation = {
46
+ ...serialized.operation,
47
+ timestamp: new Date(serialized.operation.timestamp),
48
+ };
49
+ }
50
+ if (serialized.operations) {
51
+ message.operations = serialized.operations.map(op => ({
52
+ ...op,
53
+ timestamp: new Date(op.timestamp),
54
+ }));
55
+ }
56
+ return message;
57
+ }
58
+ /**
59
+ * Global HST Operation Log Store
60
+ * Tracks all mutations with full metadata for undo/redo, sync, and audit
61
+ *
62
+ * @public
63
+ */
64
+ export const useOperationLogStore = defineStore('hst-operation-log', () => {
65
+ // Configuration
66
+ const config = ref({
67
+ maxOperations: 100,
68
+ enableCrossTabSync: true,
69
+ autoSyncInterval: 30000,
70
+ enablePersistence: false,
71
+ persistenceKeyPrefix: 'stonecrop-ops',
72
+ });
73
+ // State
74
+ const operations = ref([]);
75
+ const currentIndex = ref(-1); // Points to the last applied operation
76
+ const clientId = ref(generateId());
77
+ const batchMode = ref(false);
78
+ const batchStack = ref([]);
79
+ // Computed
80
+ const canUndo = computed(() => {
81
+ // Can undo if there are operations and we're not at the beginning
82
+ if (currentIndex.value < 0)
83
+ return false;
84
+ // Check if the operation at currentIndex is reversible
85
+ const operation = operations.value[currentIndex.value];
86
+ return operation?.reversible ?? false;
87
+ });
88
+ const canRedo = computed(() => {
89
+ // Can redo if there are operations ahead of current index
90
+ return currentIndex.value < operations.value.length - 1;
91
+ });
92
+ const undoCount = computed(() => {
93
+ let count = 0;
94
+ for (let i = currentIndex.value; i >= 0; i--) {
95
+ if (operations.value[i]?.reversible)
96
+ count++;
97
+ else
98
+ break;
99
+ }
100
+ return count;
101
+ });
102
+ const redoCount = computed(() => {
103
+ return operations.value.length - 1 - currentIndex.value;
104
+ });
105
+ const undoRedoState = computed(() => ({
106
+ canUndo: canUndo.value,
107
+ canRedo: canRedo.value,
108
+ undoCount: undoCount.value,
109
+ redoCount: redoCount.value,
110
+ currentIndex: currentIndex.value,
111
+ }));
112
+ // Core Methods
113
+ /**
114
+ * Configure the operation log
115
+ */
116
+ function configure(options) {
117
+ config.value = { ...config.value, ...options };
118
+ // Set up persistence if enabled
119
+ if (config.value.enablePersistence) {
120
+ loadFromPersistence();
121
+ setupPersistenceWatcher();
122
+ }
123
+ // Set up cross-tab sync if enabled
124
+ if (config.value.enableCrossTabSync) {
125
+ setupCrossTabSync();
126
+ }
127
+ }
128
+ /**
129
+ * Add an operation to the log
130
+ */
131
+ function addOperation(operation, source = 'user') {
132
+ const fullOperation = {
133
+ ...operation,
134
+ id: generateId(),
135
+ timestamp: new Date(),
136
+ source: source,
137
+ userId: config.value.userId,
138
+ };
139
+ // Apply filter if configured
140
+ if (config.value.operationFilter && !config.value.operationFilter(fullOperation)) {
141
+ return fullOperation.id;
142
+ }
143
+ // If in batch mode, collect operations in current batch
144
+ if (batchMode.value && batchStack.value.length > 0) {
145
+ batchStack.value[batchStack.value.length - 1].operations.push(fullOperation);
146
+ return fullOperation.id;
147
+ }
148
+ // Remove any operations after current index (they become invalid after new operation)
149
+ if (currentIndex.value < operations.value.length - 1) {
150
+ operations.value = operations.value.slice(0, currentIndex.value + 1);
151
+ }
152
+ // Add new operation
153
+ operations.value.push(fullOperation);
154
+ currentIndex.value++;
155
+ // Enforce max operations limit
156
+ if (config.value.maxOperations && operations.value.length > config.value.maxOperations) {
157
+ const overflow = operations.value.length - config.value.maxOperations;
158
+ operations.value = operations.value.slice(overflow);
159
+ currentIndex.value -= overflow;
160
+ }
161
+ // Broadcast to other tabs
162
+ if (config.value.enableCrossTabSync) {
163
+ broadcastOperation(fullOperation);
164
+ }
165
+ return fullOperation.id;
166
+ }
167
+ /**
168
+ * Start batch mode - collect multiple operations
169
+ */
170
+ function startBatch() {
171
+ batchMode.value = true;
172
+ batchStack.value.push({
173
+ id: generateId(),
174
+ operations: [],
175
+ });
176
+ }
177
+ /**
178
+ * Commit batch - create a single batch operation
179
+ */
180
+ function commitBatch(description) {
181
+ if (!batchMode.value || batchStack.value.length === 0) {
182
+ return null;
183
+ }
184
+ const currentBatchData = batchStack.value.pop();
185
+ const batchOperations = currentBatchData.operations;
186
+ if (batchOperations.length === 0) {
187
+ // Empty batch - just pop and continue if there are outer batches
188
+ if (batchStack.value.length === 0) {
189
+ batchMode.value = false;
190
+ }
191
+ return null;
192
+ }
193
+ const batchId = currentBatchData.id;
194
+ const allReversible = batchOperations.every(op => op.reversible);
195
+ // Create ancestor batch operation
196
+ const batchOperation = {
197
+ id: batchId,
198
+ type: 'batch',
199
+ path: '', // Batch doesn't have a single path
200
+ fieldname: '',
201
+ beforeValue: null,
202
+ afterValue: null,
203
+ doctype: batchOperations[0]?.doctype || '',
204
+ timestamp: new Date(),
205
+ source: 'user',
206
+ reversible: allReversible,
207
+ irreversibleReason: allReversible ? undefined : 'Contains irreversible operations',
208
+ descendantOperationIds: batchOperations.map(op => op.id),
209
+ metadata: { description },
210
+ };
211
+ // Add ancestor operation ID to all descendants
212
+ batchOperations.forEach(op => {
213
+ op.ancestorOperationId = batchId;
214
+ });
215
+ // If we're inside a ancestor batch, add this batch as a descendant of the ancestor
216
+ if (batchStack.value.length > 0) {
217
+ // Nested batch - add the batch operation to the ancestor batch
218
+ batchStack.value[batchStack.value.length - 1].operations.push(batchOperation);
219
+ }
220
+ else {
221
+ // Top-level batch - add to the operations log
222
+ operations.value.push(...batchOperations, batchOperation);
223
+ currentIndex.value = operations.value.length - 1;
224
+ }
225
+ // Broadcast batch
226
+ if (config.value.enableCrossTabSync) {
227
+ broadcastBatch(batchOperations, batchOperation);
228
+ }
229
+ // If no more batches on stack, exit batch mode
230
+ if (batchStack.value.length === 0) {
231
+ batchMode.value = false;
232
+ }
233
+ return batchId;
234
+ }
235
+ /**
236
+ * Cancel batch mode without committing
237
+ */
238
+ function cancelBatch() {
239
+ batchStack.value = [];
240
+ batchMode.value = false;
241
+ }
242
+ /**
243
+ * Undo the last operation
244
+ */
245
+ function undo(store) {
246
+ if (!canUndo.value)
247
+ return false;
248
+ const operation = operations.value[currentIndex.value];
249
+ if (!operation.reversible) {
250
+ // Warn about irreversible operation
251
+ if (typeof console !== 'undefined' && operation.irreversibleReason) {
252
+ // eslint-disable-next-line no-console
253
+ console.warn('Cannot undo irreversible operation:', operation.irreversibleReason);
254
+ }
255
+ return false;
256
+ }
257
+ try {
258
+ // Handle batch operations
259
+ if (operation.type === 'batch' && operation.descendantOperationIds) {
260
+ // Undo all descendant operations in reverse order
261
+ for (let i = operation.descendantOperationIds.length - 1; i >= 0; i--) {
262
+ const descendantId = operation.descendantOperationIds[i];
263
+ const descendantOp = operations.value.find(op => op.id === descendantId);
264
+ if (descendantOp) {
265
+ revertOperation(descendantOp, store);
266
+ }
267
+ }
268
+ }
269
+ else {
270
+ // Undo single operation
271
+ revertOperation(operation, store);
272
+ }
273
+ currentIndex.value--;
274
+ // Broadcast undo to other tabs
275
+ if (config.value.enableCrossTabSync) {
276
+ broadcastUndo(operation);
277
+ }
278
+ return true;
279
+ }
280
+ catch (error) {
281
+ // Log error in development
282
+ if (typeof console !== 'undefined') {
283
+ // eslint-disable-next-line no-console
284
+ console.error('Undo failed:', error);
285
+ }
286
+ return false;
287
+ }
288
+ }
289
+ /**
290
+ * Redo the next operation
291
+ */
292
+ function redo(store) {
293
+ if (!canRedo.value)
294
+ return false;
295
+ const operation = operations.value[currentIndex.value + 1];
296
+ try {
297
+ // Handle batch operations
298
+ if (operation.type === 'batch' && operation.descendantOperationIds) {
299
+ // Redo all descendant operations in order
300
+ for (const descendantId of operation.descendantOperationIds) {
301
+ const descendantOp = operations.value.find(op => op.id === descendantId);
302
+ if (descendantOp) {
303
+ applyOperation(descendantOp, store);
304
+ }
305
+ }
306
+ }
307
+ else {
308
+ // Redo single operation
309
+ applyOperation(operation, store);
310
+ }
311
+ currentIndex.value++;
312
+ // Broadcast redo to other tabs
313
+ if (config.value.enableCrossTabSync) {
314
+ broadcastRedo(operation);
315
+ }
316
+ return true;
317
+ }
318
+ catch (error) {
319
+ // Log error in development
320
+ if (typeof console !== 'undefined') {
321
+ // eslint-disable-next-line no-console
322
+ console.error('Redo failed:', error);
323
+ }
324
+ return false;
325
+ }
326
+ }
327
+ /**
328
+ * Revert an operation (apply beforeValue)
329
+ */
330
+ function revertOperation(operation, store) {
331
+ // Both 'set' and 'delete' operations can be reverted by setting to beforeValue
332
+ if ((operation.type === 'set' || operation.type === 'delete') && store && typeof store.set === 'function') {
333
+ store.set(operation.path, operation.beforeValue, 'undo');
334
+ }
335
+ // Note: 'transition' operations are marked as non-reversible, so they won't reach here
336
+ // Note: 'batch' operations are handled separately in the undo function
337
+ }
338
+ /**
339
+ * Apply an operation (apply afterValue)
340
+ */
341
+ function applyOperation(operation, store) {
342
+ // Both 'set' and 'delete' operations can be applied by setting to afterValue
343
+ if ((operation.type === 'set' || operation.type === 'delete') && store && typeof store.set === 'function') {
344
+ store.set(operation.path, operation.afterValue, 'redo');
345
+ }
346
+ // Note: 'transition' operations are marked as non-reversible, so they won't reach here
347
+ // Note: 'batch' operations are handled separately in the redo function
348
+ }
349
+ /**
350
+ * Get operation log snapshot for debugging
351
+ */
352
+ function getSnapshot() {
353
+ const reversibleOps = operations.value.filter(op => op.reversible).length;
354
+ const timestamps = operations.value.map(op => op.timestamp);
355
+ return {
356
+ operations: [...operations.value],
357
+ currentIndex: currentIndex.value,
358
+ totalOperations: operations.value.length,
359
+ reversibleOperations: reversibleOps,
360
+ irreversibleOperations: operations.value.length - reversibleOps,
361
+ oldestOperation: timestamps.length > 0 ? new Date(Math.min(...timestamps.map(t => t.getTime()))) : undefined,
362
+ newestOperation: timestamps.length > 0 ? new Date(Math.max(...timestamps.map(t => t.getTime()))) : undefined,
363
+ };
364
+ }
365
+ /**
366
+ * Clear all operations
367
+ */
368
+ function clear() {
369
+ operations.value = [];
370
+ currentIndex.value = -1;
371
+ }
372
+ /**
373
+ * Get operations for a specific doctype and recordId
374
+ */
375
+ function getOperationsFor(doctype, recordId) {
376
+ return operations.value.filter(op => op.doctype === doctype && (recordId === undefined || op.recordId === recordId));
377
+ }
378
+ /**
379
+ * Mark an operation as irreversible
380
+ * @param operationId - The ID of the operation to mark
381
+ * @param reason - The reason why the operation is irreversible
382
+ */
383
+ function markIrreversible(operationId, reason) {
384
+ const operation = operations.value.find(op => op.id === operationId);
385
+ if (operation) {
386
+ operation.reversible = false;
387
+ operation.irreversibleReason = reason;
388
+ }
389
+ }
390
+ /**
391
+ * Log an action execution (stateless actions like print, email, etc.)
392
+ * These operations are tracked but typically not reversible
393
+ * @param doctype - The doctype the action was executed on
394
+ * @param actionName - The name of the action that was executed
395
+ * @param recordIds - Optional array of record IDs the action was executed on
396
+ * @param result - The result of the action execution
397
+ * @param error - Optional error message if action failed
398
+ * @returns The operation ID
399
+ */
400
+ function logAction(doctype, actionName, recordIds, result = 'success', error) {
401
+ const operation = {
402
+ type: 'action',
403
+ path: recordIds && recordIds.length > 0 ? `${doctype}.${recordIds[0]}` : doctype,
404
+ fieldname: '',
405
+ beforeValue: null,
406
+ afterValue: null,
407
+ doctype,
408
+ recordId: recordIds && recordIds.length > 0 ? recordIds[0] : undefined,
409
+ reversible: false, // Actions are typically not reversible
410
+ actionName,
411
+ actionRecordIds: recordIds,
412
+ actionResult: result,
413
+ actionError: error,
414
+ };
415
+ return addOperation(operation);
416
+ }
417
+ // Cross-tab synchronization
418
+ let broadcastChannel = null;
419
+ function setupCrossTabSync() {
420
+ if (typeof window === 'undefined' || !window.BroadcastChannel)
421
+ return;
422
+ broadcastChannel = new BroadcastChannel('stonecrop-operation-log');
423
+ broadcastChannel.addEventListener('message', (event) => {
424
+ const rawMessage = event.data;
425
+ if (!rawMessage || typeof rawMessage !== 'object')
426
+ return;
427
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
428
+ const message = deserializeFromBroadcast(rawMessage);
429
+ // Ignore messages from this tab
430
+ if (message.clientId === clientId.value)
431
+ return;
432
+ if (message.type === 'operation' && message.operation) {
433
+ // Add operation from another tab
434
+ operations.value.push({ ...message.operation, source: 'sync' });
435
+ currentIndex.value = operations.value.length - 1;
436
+ }
437
+ else if (message.type === 'operation' && message.operations) {
438
+ // Add batch operations from another tab
439
+ operations.value.push(...message.operations.map(op => ({ ...op, source: 'sync' })));
440
+ currentIndex.value = operations.value.length - 1;
441
+ }
442
+ });
443
+ }
444
+ function broadcastOperation(operation) {
445
+ if (!broadcastChannel)
446
+ return;
447
+ const message = {
448
+ type: 'operation',
449
+ operation,
450
+ clientId: clientId.value,
451
+ timestamp: new Date(),
452
+ };
453
+ broadcastChannel.postMessage(serializeForBroadcast(message));
454
+ }
455
+ function broadcastBatch(descendantOps, batchOp) {
456
+ if (!broadcastChannel)
457
+ return;
458
+ const message = {
459
+ type: 'operation',
460
+ operations: [...descendantOps, batchOp],
461
+ clientId: clientId.value,
462
+ timestamp: new Date(),
463
+ };
464
+ broadcastChannel.postMessage(serializeForBroadcast(message));
465
+ }
466
+ function broadcastUndo(operation) {
467
+ if (!broadcastChannel)
468
+ return;
469
+ const message = {
470
+ type: 'undo',
471
+ operation,
472
+ clientId: clientId.value,
473
+ timestamp: new Date(),
474
+ };
475
+ broadcastChannel.postMessage(serializeForBroadcast(message));
476
+ }
477
+ function broadcastRedo(operation) {
478
+ if (!broadcastChannel)
479
+ return;
480
+ const message = {
481
+ type: 'redo',
482
+ operation,
483
+ clientId: clientId.value,
484
+ timestamp: new Date(),
485
+ };
486
+ broadcastChannel.postMessage(serializeForBroadcast(message));
487
+ }
488
+ const persistedData = useLocalStorage('stonecrop-operations', null, {
489
+ serializer: {
490
+ read: (v) => {
491
+ try {
492
+ const data = JSON.parse(v);
493
+ return data;
494
+ }
495
+ catch {
496
+ return null;
497
+ }
498
+ },
499
+ write: (v) => {
500
+ if (!v)
501
+ return '';
502
+ return JSON.stringify(v);
503
+ },
504
+ },
505
+ });
506
+ function loadFromPersistence() {
507
+ if (typeof window === 'undefined')
508
+ return;
509
+ try {
510
+ const data = persistedData.value;
511
+ if (data && Array.isArray(data.operations)) {
512
+ operations.value = data.operations.map(op => ({
513
+ ...op,
514
+ timestamp: new Date(op.timestamp),
515
+ }));
516
+ currentIndex.value = data.currentIndex ?? -1;
517
+ }
518
+ }
519
+ catch (error) {
520
+ // Log error in development
521
+ if (typeof console !== 'undefined') {
522
+ // eslint-disable-next-line no-console
523
+ console.error('Failed to load operations from persistence:', error);
524
+ }
525
+ }
526
+ }
527
+ function saveToPersistence() {
528
+ if (typeof window === 'undefined')
529
+ return;
530
+ try {
531
+ persistedData.value = {
532
+ operations: operations.value.map(op => ({
533
+ ...op,
534
+ timestamp: op.timestamp.toISOString(),
535
+ })),
536
+ currentIndex: currentIndex.value,
537
+ };
538
+ }
539
+ catch (error) {
540
+ // Log error in development
541
+ if (typeof console !== 'undefined') {
542
+ // eslint-disable-next-line no-console
543
+ console.error('Failed to save operations to persistence:', error);
544
+ }
545
+ }
546
+ }
547
+ function setupPersistenceWatcher() {
548
+ watch([operations, currentIndex], () => {
549
+ if (config.value.enablePersistence) {
550
+ saveToPersistence();
551
+ }
552
+ }, { deep: true });
553
+ }
554
+ return {
555
+ // State
556
+ operations,
557
+ currentIndex,
558
+ config,
559
+ clientId,
560
+ undoRedoState,
561
+ // Computed
562
+ canUndo,
563
+ canRedo,
564
+ undoCount,
565
+ redoCount,
566
+ // Methods
567
+ configure,
568
+ addOperation,
569
+ startBatch,
570
+ commitBatch,
571
+ cancelBatch,
572
+ undo,
573
+ redo,
574
+ clear,
575
+ getOperationsFor,
576
+ getSnapshot,
577
+ markIrreversible,
578
+ logAction,
579
+ };
580
+ });
@@ -0,0 +1,29 @@
1
+ import { defineStore } from 'pinia';
2
+ import { xstate } from 'pinia-xstate';
3
+ import { createMachine } from 'xstate';
4
+ export const counterMachine = createMachine({
5
+ id: 'counter',
6
+ initial: 'active',
7
+ context: {
8
+ count: 0,
9
+ },
10
+ states: {
11
+ active: {
12
+ on: {
13
+ INC: { actions: 'increment' },
14
+ DEC: { actions: 'decrement' },
15
+ },
16
+ },
17
+ },
18
+ }, {
19
+ actions: {
20
+ increment: context => {
21
+ context.count = context.count + 1;
22
+ },
23
+ decrement: context => {
24
+ context.count = context.count - 1;
25
+ },
26
+ },
27
+ });
28
+ // create a store using the xstate middleware
29
+ export const useCounterStore = defineStore(counterMachine.id, xstate(counterMachine));
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Vitest setup file
3
+ * Runs before all test files
4
+ */
5
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../tests/setup.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Vitest setup file
3
+ * Runs before all test files
4
+ */
5
+ // Remove Node.js's native BroadcastChannel to avoid conflicts with mocks
6
+ // Tests that need BroadcastChannel will mock it themselves
7
+ if (typeof global !== 'undefined' && 'BroadcastChannel' in global) {
8
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
9
+ delete global.BroadcastChannel;
10
+ }
11
+ // Also remove from globalThis if it exists there
12
+ if (typeof globalThis !== 'undefined' && 'BroadcastChannel' in globalThis) {
13
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
14
+ delete globalThis.BroadcastChannel;
15
+ }
File without changes
File without changes
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Field-level action trigger system types
3
+ * @public
4
+ */
File without changes
@@ -0,0 +1,10 @@
1
+ // Re-export all type files
2
+ export * from './composable';
3
+ export * from './doctype';
4
+ export * from './field-triggers';
5
+ export * from './hst';
6
+ export * from './operation-log';
7
+ export * from './plugin';
8
+ export * from './registry';
9
+ export * from './schema-validator';
10
+ export * from './stonecrop';
File without changes
File without changes
File without changes
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Validation severity levels
3
+ * @public
4
+ */
5
+ export var ValidationSeverity;
6
+ (function (ValidationSeverity) {
7
+ /** Blocking error that prevents save */
8
+ ValidationSeverity["ERROR"] = "error";
9
+ /** Advisory warning that allows save */
10
+ ValidationSeverity["WARNING"] = "warning";
11
+ /** Informational message */
12
+ ValidationSeverity["INFO"] = "info";
13
+ })(ValidationSeverity || (ValidationSeverity = {}));
File without changes