@mmapp/react 0.1.0-alpha.1 → 0.1.0-alpha.4

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 (94) hide show
  1. package/README.md +112 -0
  2. package/dist/index.d.mts +1378 -94
  3. package/dist/index.d.ts +1378 -94
  4. package/dist/index.js +1094 -1309
  5. package/dist/index.mjs +1038 -1296
  6. package/package.json +4 -3
  7. package/package.json.backup +0 -41
  8. package/src/Blueprint.ts +0 -216
  9. package/src/__tests__/Blueprint.test.ts +0 -106
  10. package/src/__tests__/action-context.test.ts +0 -166
  11. package/src/__tests__/actionCreators.test.ts +0 -179
  12. package/src/__tests__/builders.test.ts +0 -336
  13. package/src/__tests__/defineBlueprint-composition.test.ts +0 -106
  14. package/src/__tests__/factories.test.ts +0 -229
  15. package/src/__tests__/loader.test.ts +0 -159
  16. package/src/__tests__/logger.test.ts +0 -70
  17. package/src/__tests__/type-inference.test.ts +0 -160
  18. package/src/__tests__/typed-transitions.test.ts +0 -126
  19. package/src/__tests__/useModuleConfig.test.ts +0 -61
  20. package/src/actionCreators.ts +0 -132
  21. package/src/actions.ts +0 -547
  22. package/src/atoms/index.ts +0 -600
  23. package/src/authoring.ts +0 -92
  24. package/src/browser-player.ts +0 -783
  25. package/src/builders.ts +0 -1342
  26. package/src/components/ExperienceWorkflowBridge.tsx +0 -123
  27. package/src/components/PlayerProvider.tsx +0 -43
  28. package/src/components/atoms/index.tsx +0 -269
  29. package/src/components/index.ts +0 -36
  30. package/src/conditions.ts +0 -692
  31. package/src/config/defineBlueprint.ts +0 -329
  32. package/src/config/defineModel.ts +0 -753
  33. package/src/config/defineWorkspace.ts +0 -24
  34. package/src/core/WorkflowRuntime.ts +0 -153
  35. package/src/factories.ts +0 -425
  36. package/src/grammar/index.ts +0 -173
  37. package/src/hooks/index.ts +0 -106
  38. package/src/hooks/useAuth.ts +0 -288
  39. package/src/hooks/useChannel.ts +0 -304
  40. package/src/hooks/useComputed.ts +0 -154
  41. package/src/hooks/useDomainSubscription.ts +0 -110
  42. package/src/hooks/useDuringAction.ts +0 -99
  43. package/src/hooks/useExperienceState.ts +0 -59
  44. package/src/hooks/useExpressionLibrary.ts +0 -129
  45. package/src/hooks/useForm.ts +0 -352
  46. package/src/hooks/useGeolocation.ts +0 -207
  47. package/src/hooks/useMapView.ts +0 -259
  48. package/src/hooks/useMiddleware.ts +0 -291
  49. package/src/hooks/useModel.ts +0 -363
  50. package/src/hooks/useModule.ts +0 -59
  51. package/src/hooks/useModuleConfig.ts +0 -61
  52. package/src/hooks/useMutation.ts +0 -237
  53. package/src/hooks/useNotification.ts +0 -151
  54. package/src/hooks/useOnChange.ts +0 -30
  55. package/src/hooks/useOnEnter.ts +0 -59
  56. package/src/hooks/useOnEvent.ts +0 -37
  57. package/src/hooks/useOnExit.ts +0 -27
  58. package/src/hooks/useOnTransition.ts +0 -30
  59. package/src/hooks/usePackage.ts +0 -128
  60. package/src/hooks/useParams.ts +0 -33
  61. package/src/hooks/usePlayer.ts +0 -308
  62. package/src/hooks/useQuery.ts +0 -184
  63. package/src/hooks/useRealtimeQuery.ts +0 -222
  64. package/src/hooks/useRole.ts +0 -191
  65. package/src/hooks/useRouteParams.ts +0 -100
  66. package/src/hooks/useRouter.ts +0 -347
  67. package/src/hooks/useServerAction.ts +0 -178
  68. package/src/hooks/useServerState.ts +0 -284
  69. package/src/hooks/useToast.ts +0 -164
  70. package/src/hooks/useTransition.ts +0 -39
  71. package/src/hooks/useView.ts +0 -102
  72. package/src/hooks/useWhileIn.ts +0 -48
  73. package/src/hooks/useWorkflow.ts +0 -63
  74. package/src/index.ts +0 -465
  75. package/src/loader/experience-workflow-loader.ts +0 -192
  76. package/src/loader/index.ts +0 -6
  77. package/src/local/LocalEngine.ts +0 -388
  78. package/src/local/LocalEngineAdapter.ts +0 -175
  79. package/src/local/LocalEngineContext.ts +0 -30
  80. package/src/logger.ts +0 -37
  81. package/src/mixins.ts +0 -1160
  82. package/src/providers/RuntimeContext.ts +0 -20
  83. package/src/providers/WorkflowProvider.tsx +0 -28
  84. package/src/routing/instance-key.ts +0 -107
  85. package/src/server/transition-context.ts +0 -172
  86. package/src/testing/index.ts +0 -9
  87. package/src/testing/useBlueprintTestRunner.ts +0 -91
  88. package/src/testing/useGraphAnalysis.ts +0 -18
  89. package/src/testing/useTestRunner.ts +0 -77
  90. package/src/testing.ts +0 -995
  91. package/src/types/workflow-inference.ts +0 -158
  92. package/src/types.ts +0 -114
  93. package/tsconfig.json +0 -27
  94. package/vitest.config.ts +0 -8
@@ -1,59 +0,0 @@
1
- /**
2
- * useExperienceState — Reactive accessor for experience workflow state.
3
- *
4
- * Provides fine-grained access to the player's state with optional
5
- * selector for render optimization. Components only re-render when
6
- * their selected slice of state actually changes.
7
- */
8
-
9
- import { useCallback, useRef } from 'react';
10
- import type { PlayerHandle } from '../types';
11
-
12
- type Selector<T> = (state: {
13
- currentState: string;
14
- stateData: Record<string, unknown>;
15
- status: string;
16
- availableTransitions: string[];
17
- }) => T;
18
-
19
- /**
20
- * Subscribe to a slice of player state with a selector.
21
- *
22
- * @example
23
- * ```tsx
24
- * const title = useExperienceState(player, s => s.stateData.title as string);
25
- * const canAdvance = useExperienceState(player, s => s.availableTransitions.includes('advance'));
26
- * ```
27
- */
28
- export function useExperienceState<T>(player: PlayerHandle, selector: Selector<T>): T {
29
- const selectorRef = useRef(selector);
30
- selectorRef.current = selector;
31
-
32
- const getSnapshot = useCallback(() => {
33
- return selectorRef.current({
34
- currentState: player.currentState,
35
- stateData: player.stateData,
36
- status: player.status,
37
- availableTransitions: player.availableTransitions,
38
- });
39
- }, [player.currentState, player.stateData, player.status, player.availableTransitions]);
40
-
41
- return getSnapshot();
42
- }
43
-
44
- /**
45
- * Access a single field from state_data.
46
- *
47
- * @example
48
- * ```tsx
49
- * const count = useStateField(player, 'count', 0);
50
- * ```
51
- */
52
- export function useStateField<T = unknown>(
53
- player: PlayerHandle,
54
- field: string,
55
- defaultValue?: T,
56
- ): T {
57
- const value = player.stateData[field];
58
- return (value !== undefined ? value : defaultValue) as T;
59
- }
@@ -1,129 +0,0 @@
1
- /**
2
- * useExpressionLibrary — Load an expression library by slug
3
- *
4
- * Fetches a workflow definition with category='expression-library' and
5
- * returns its expression + metadata for use in computed fields.
6
- * Supports I3 expression library system.
7
- *
8
- * Usage in .workflow.tsx:
9
- * const { expression, functions, loading } = useExpressionLibrary('pricing/bulk-discount');
10
- * // Use in computed field: call "pricing/bulk-discount"(subtotal, quantity)
11
- */
12
-
13
- import { useState, useEffect, useCallback, useRef } from 'react';
14
-
15
- // =============================================================================
16
- // Types
17
- // =============================================================================
18
-
19
- export interface ExpressionLibraryResult {
20
- /** The expression string from the library. */
21
- expression: string | null;
22
- /** Library name. */
23
- name: string | null;
24
- /** Library description. */
25
- description: string | null;
26
- /** Parameter signature (from metadata). */
27
- params: LibraryParam[];
28
- /** Return type hint (from metadata). */
29
- returnType: string | null;
30
- /** Whether the library is currently loading. */
31
- loading: boolean;
32
- /** Error from the last fetch attempt. */
33
- error: Error | null;
34
- /** Manually trigger a refetch. */
35
- refetch: () => Promise<void>;
36
- }
37
-
38
- export interface LibraryParam {
39
- name: string;
40
- type?: string;
41
- description?: string;
42
- }
43
-
44
- export interface ExpressionLibraryRecord {
45
- id: string;
46
- slug: string;
47
- name: string;
48
- description?: string;
49
- metadata?: {
50
- expression?: string;
51
- params?: LibraryParam[];
52
- returnType?: string;
53
- };
54
- }
55
-
56
- /**
57
- * Expression library resolver — the runtime must provide an implementation.
58
- */
59
- export interface ExpressionLibraryResolver {
60
- getLibrary: (slug: string) => Promise<ExpressionLibraryRecord | null>;
61
- }
62
-
63
- // Global resolver (set by provider)
64
- let _globalExpressionLibraryResolver: ExpressionLibraryResolver | null = null;
65
-
66
- export function setExpressionLibraryResolver(resolver: ExpressionLibraryResolver | null): void {
67
- _globalExpressionLibraryResolver = resolver;
68
- }
69
-
70
- // =============================================================================
71
- // Hook
72
- // =============================================================================
73
-
74
- /**
75
- * Fetches an expression library definition by slug.
76
- *
77
- * @param slug - The expression library slug (e.g., 'pricing/bulk-discount').
78
- * @returns Library result with expression, params, loading state, and error.
79
- */
80
- export function useExpressionLibrary(slug: string | null): ExpressionLibraryResult {
81
- const [record, setRecord] = useState<ExpressionLibraryRecord | null>(null);
82
- const [loading, setLoading] = useState(!!slug);
83
- const [error, setError] = useState<Error | null>(null);
84
- const slugRef = useRef(slug);
85
- slugRef.current = slug;
86
-
87
- const fetchLibrary = useCallback(async () => {
88
- const s = slugRef.current;
89
- if (!s) {
90
- setRecord(null);
91
- setLoading(false);
92
- return;
93
- }
94
-
95
- const resolver = _globalExpressionLibraryResolver;
96
- if (!resolver) {
97
- setError(new Error('useExpressionLibrary: No resolver configured. Wrap your app in a WorkflowProvider.'));
98
- setLoading(false);
99
- return;
100
- }
101
-
102
- try {
103
- setLoading(true);
104
- setError(null);
105
- const result = await resolver.getLibrary(s);
106
- setRecord(result);
107
- } catch (err) {
108
- setError(err instanceof Error ? err : new Error(String(err)));
109
- } finally {
110
- setLoading(false);
111
- }
112
- }, []);
113
-
114
- useEffect(() => {
115
- fetchLibrary();
116
- }, [slug, fetchLibrary]);
117
-
118
- const handle: ExpressionLibraryResult = {
119
- expression: record?.metadata?.expression ?? null,
120
- name: record?.name ?? null,
121
- description: record?.description ?? null,
122
- params: record?.metadata?.params ?? [],
123
- returnType: record?.metadata?.returnType ?? null,
124
- loading,
125
- error,
126
- refetch: fetchLibrary,
127
- };
128
- return handle;
129
- }
@@ -1,352 +0,0 @@
1
- /**
2
- * useForm — Form state management with validation, dirty tracking, and submit handling.
3
- *
4
- * Provides a complete form solution with field-level and form-level validation,
5
- * dirty/touched tracking, and async submit handling.
6
- *
7
- * Usage in .workflow.tsx:
8
- * const form = useForm({
9
- * initialValues: { pickup: '', destination: '', rideType: 'standard' },
10
- * validate: (values) => {
11
- * const errors: Record<string, string> = {};
12
- * if (!values.pickup) errors.pickup = 'Pickup location is required';
13
- * if (!values.destination) errors.destination = 'Destination is required';
14
- * return errors;
15
- * },
16
- * onSubmit: async (values) => {
17
- * await createRide(values);
18
- * },
19
- * });
20
- *
21
- * <TextInput bind="pickup" {...form.register('pickup')} />
22
- * <Button onClick={form.handleSubmit} disabled={!form.isValid}>
23
- * Request Ride
24
- * </Button>
25
- */
26
-
27
- import { useState, useCallback, useMemo, useRef } from 'react';
28
-
29
- // =============================================================================
30
- // Types
31
- // =============================================================================
32
-
33
- /** Validation function. Returns error messages keyed by field name. */
34
- export type FormValidator<T extends Record<string, unknown>> = (
35
- values: T,
36
- ) => Record<string, string> | Promise<Record<string, string>>;
37
-
38
- /** Field-level validator. Returns error message or empty string. */
39
- export type FieldValidator = (value: unknown, fieldName: string) => string | Promise<string>;
40
-
41
- /** Form configuration. */
42
- export interface FormConfig<T extends Record<string, unknown>> {
43
- /** Initial form values. */
44
- initialValues: T;
45
- /** Form-level validation function. */
46
- validate?: FormValidator<T>;
47
- /** Field-level validators. */
48
- fieldValidators?: Partial<Record<keyof T, FieldValidator>>;
49
- /** Called on valid form submission. */
50
- onSubmit?: (values: T) => void | Promise<void>;
51
- /** Called when submit fails validation. */
52
- onError?: (errors: Record<string, string>) => void;
53
- /** Validate on every change (default: false). */
54
- validateOnChange?: boolean;
55
- /** Validate on blur (default: true). */
56
- validateOnBlur?: boolean;
57
- }
58
-
59
- /** Field registration props (spread onto input components). */
60
- export interface FieldRegistration {
61
- value: unknown;
62
- onChange: (valueOrEvent: unknown) => void;
63
- onBlur: () => void;
64
- name: string;
65
- error?: string;
66
- }
67
-
68
- /** Form handle returned by useForm. */
69
- export interface FormHandle<T extends Record<string, unknown>> {
70
- /** Current form values. */
71
- values: T;
72
- /** Current validation errors (keyed by field name). */
73
- errors: Record<string, string>;
74
- /** Which fields have been touched (blurred). */
75
- touched: Record<string, boolean>;
76
- /** Whether any field has been modified from initial values. */
77
- dirty: boolean;
78
- /** Which specific fields are dirty. */
79
- dirtyFields: Record<string, boolean>;
80
- /** Whether the form passes validation. */
81
- isValid: boolean;
82
- /** Whether the form is currently submitting. */
83
- isSubmitting: boolean;
84
- /** Number of times the form has been submitted. */
85
- submitCount: number;
86
- /** Set a specific field's value. */
87
- setField: (name: keyof T, value: unknown) => void;
88
- /** Set a specific field's error. */
89
- setError: (name: string, message: string) => void;
90
- /** Clear a field's error. */
91
- clearError: (name: string) => void;
92
- /** Set multiple values at once. */
93
- setValues: (values: Partial<T>) => void;
94
- /** Reset form to initial values. */
95
- reset: (newInitial?: T) => void;
96
- /** Trigger form submission. */
97
- handleSubmit: () => Promise<void>;
98
- /** Validate the entire form manually. */
99
- validateForm: () => Promise<Record<string, string>>;
100
- /** Register a field (returns props to spread on input). */
101
- register: (name: keyof T) => FieldRegistration;
102
- /** Get a specific field's value. */
103
- getField: (name: keyof T) => unknown;
104
- }
105
-
106
- // =============================================================================
107
- // Hook
108
- // =============================================================================
109
-
110
- /**
111
- * Form state management with validation and submit handling.
112
- *
113
- * @param config - Form configuration with initial values, validators, and submit handler.
114
- * @returns Form handle with values, errors, and control methods.
115
- */
116
- export function useForm<T extends Record<string, unknown>>(
117
- config: FormConfig<T>,
118
- ): FormHandle<T> {
119
- const {
120
- initialValues,
121
- validate,
122
- fieldValidators,
123
- onSubmit,
124
- onError,
125
- validateOnChange = false,
126
- validateOnBlur = true,
127
- } = config;
128
-
129
- const [values, setValuesState] = useState<T>(initialValues);
130
- const [errors, setErrors] = useState<Record<string, string>>({});
131
- const [touched, setTouched] = useState<Record<string, boolean>>({});
132
- const [isSubmitting, setIsSubmitting] = useState(false);
133
- const [submitCount, setSubmitCount] = useState(0);
134
-
135
- const initialRef = useRef(initialValues);
136
- const validateRef = useRef(validate);
137
- validateRef.current = validate;
138
- const fieldValidatorsRef = useRef(fieldValidators);
139
- fieldValidatorsRef.current = fieldValidators;
140
- const onSubmitRef = useRef(onSubmit);
141
- onSubmitRef.current = onSubmit;
142
- const onErrorRef = useRef(onError);
143
- onErrorRef.current = onError;
144
-
145
- // Compute dirty fields
146
- const dirtyFields = useMemo(() => {
147
- const result: Record<string, boolean> = {};
148
- for (const key of Object.keys(values)) {
149
- result[key] = values[key] !== initialRef.current[key as keyof T];
150
- }
151
- return result;
152
- }, [values]);
153
-
154
- const dirty = useMemo(
155
- () => Object.values(dirtyFields).some(Boolean),
156
- [dirtyFields],
157
- );
158
-
159
- const isValid = useMemo(
160
- () => Object.keys(errors).length === 0,
161
- [errors],
162
- );
163
-
164
- // Validate a single field
165
- const validateField = useCallback(
166
- async (name: string, value: unknown): Promise<string> => {
167
- const validator = fieldValidatorsRef.current?.[name as keyof T];
168
- if (validator) {
169
- return await validator(value, name);
170
- }
171
- return '';
172
- },
173
- [],
174
- );
175
-
176
- // Validate entire form
177
- const validateForm = useCallback(async (): Promise<Record<string, string>> => {
178
- let formErrors: Record<string, string> = {};
179
-
180
- // Run form-level validator
181
- if (validateRef.current) {
182
- formErrors = await validateRef.current(values);
183
- }
184
-
185
- // Run field-level validators
186
- if (fieldValidatorsRef.current) {
187
- for (const [name, validator] of Object.entries(fieldValidatorsRef.current)) {
188
- if (validator && !formErrors[name]) {
189
- const fieldError = await (validator as FieldValidator)(values[name as keyof T], name);
190
- if (fieldError) {
191
- formErrors[name] = fieldError;
192
- }
193
- }
194
- }
195
- }
196
-
197
- setErrors(formErrors);
198
- return formErrors;
199
- }, [values]);
200
-
201
- const setField = useCallback(
202
- (name: keyof T, value: unknown) => {
203
- setValuesState((prev) => ({ ...prev, [name]: value }));
204
-
205
- if (validateOnChange) {
206
- validateField(name as string, value).then((fieldError) => {
207
- setErrors((prev) => {
208
- if (fieldError) {
209
- return { ...prev, [name]: fieldError };
210
- }
211
- const next = { ...prev };
212
- delete next[name as string];
213
- return next;
214
- });
215
- });
216
- }
217
- },
218
- [validateOnChange, validateField],
219
- );
220
-
221
- const setError = useCallback((name: string, message: string) => {
222
- setErrors((prev) => ({ ...prev, [name]: message }));
223
- }, []);
224
-
225
- const clearError = useCallback((name: string) => {
226
- setErrors((prev) => {
227
- const next = { ...prev };
228
- delete next[name];
229
- return next;
230
- });
231
- }, []);
232
-
233
- const setValues = useCallback((partial: Partial<T>) => {
234
- setValuesState((prev) => ({ ...prev, ...partial }));
235
- }, []);
236
-
237
- const reset = useCallback((newInitial?: T) => {
238
- const resetTo = newInitial ?? initialRef.current;
239
- if (newInitial) {
240
- initialRef.current = newInitial;
241
- }
242
- setValuesState(resetTo);
243
- setErrors({});
244
- setTouched({});
245
- setIsSubmitting(false);
246
- }, []);
247
-
248
- const handleSubmit = useCallback(async () => {
249
- setSubmitCount((c) => c + 1);
250
-
251
- // Touch all fields
252
- const allTouched: Record<string, boolean> = {};
253
- for (const key of Object.keys(values)) {
254
- allTouched[key] = true;
255
- }
256
- setTouched(allTouched);
257
-
258
- // Validate
259
- const formErrors = await validateForm();
260
- if (Object.keys(formErrors).length > 0) {
261
- onErrorRef.current?.(formErrors);
262
- return;
263
- }
264
-
265
- // Submit
266
- if (!onSubmitRef.current) return;
267
-
268
- setIsSubmitting(true);
269
- try {
270
- await onSubmitRef.current(values);
271
- } catch (err) {
272
- const submitError = err instanceof Error ? err : new Error(String(err));
273
- setErrors((prev) => ({ ...prev, _form: submitError.message }));
274
- } finally {
275
- setIsSubmitting(false);
276
- }
277
- }, [values, validateForm]);
278
-
279
- const register = useCallback(
280
- (name: keyof T): FieldRegistration => {
281
- return {
282
- name: name as string,
283
- value: values[name],
284
- onChange: (valueOrEvent: unknown) => {
285
- // Handle both raw values and React synthetic events
286
- let newValue: unknown;
287
- if (
288
- valueOrEvent &&
289
- typeof valueOrEvent === 'object' &&
290
- 'target' in valueOrEvent
291
- ) {
292
- const target = (valueOrEvent as { target: { type?: string; checked?: boolean; value?: unknown } }).target;
293
- newValue = target.type === 'checkbox' ? target.checked : target.value;
294
- } else {
295
- newValue = valueOrEvent;
296
- }
297
- setField(name, newValue);
298
- },
299
- onBlur: () => {
300
- setTouched((prev) => ({ ...prev, [name]: true }));
301
-
302
- if (validateOnBlur) {
303
- validateField(name as string, values[name]).then((fieldError) => {
304
- setErrors((prev) => {
305
- if (fieldError) {
306
- return { ...prev, [name]: fieldError };
307
- }
308
- const next = { ...prev };
309
- delete next[name as string];
310
- return next;
311
- });
312
- });
313
- }
314
- },
315
- error: errors[name as string],
316
- };
317
- },
318
- [values, errors, setField, validateOnBlur, validateField],
319
- );
320
-
321
- const getField = useCallback(
322
- (name: keyof T): unknown => values[name],
323
- [values],
324
- );
325
-
326
- return useMemo(
327
- (): FormHandle<T> => ({
328
- values,
329
- errors,
330
- touched,
331
- dirty,
332
- dirtyFields,
333
- isValid,
334
- isSubmitting,
335
- submitCount,
336
- setField,
337
- setError,
338
- clearError,
339
- setValues,
340
- reset,
341
- handleSubmit,
342
- validateForm,
343
- register,
344
- getField,
345
- }),
346
- [
347
- values, errors, touched, dirty, dirtyFields, isValid, isSubmitting,
348
- submitCount, setField, setError, clearError, setValues, reset,
349
- handleSubmit, validateForm, register, getField,
350
- ],
351
- );
352
- }