@almadar/runtime 4.10.2 → 4.11.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.
@@ -671,6 +671,70 @@ declare function parseNamespacedEvent(eventName: string): {
671
671
  event: string;
672
672
  };
673
673
 
674
+ /**
675
+ * PersistenceAdapter — the storage contract for runtime effect handlers.
676
+ *
677
+ * The server-side runtime and the in-browser mock runtime both invoke
678
+ * `fetch` / `persist` / `ref` / `deref` / `swap!` effects against an
679
+ * implementation of this interface. Extracted from
680
+ * `OrbitalServerRuntime.ts` so it can be imported by browser code that
681
+ * cannot depend on the server module (which pulls in express).
682
+ *
683
+ * @packageDocumentation
684
+ */
685
+
686
+ /**
687
+ * Storage contract for CRUD operations on runtime entity rows.
688
+ *
689
+ * Implementations:
690
+ * - `InMemoryPersistence` (this file) — simple Map-backed store, used by
691
+ * the browser mock runtime and as the default when an adapter is not
692
+ * supplied to `OrbitalServerRuntime`.
693
+ * - `MockPersistenceAdapter` — in-memory with faker-generated seed data
694
+ * for realistic preview content.
695
+ * - `LocalPersistenceAdapter` — localStorage-backed, browser-safe.
696
+ * - Consumer-provided (e.g. Firestore, Postgres) for production servers.
697
+ */
698
+ interface PersistenceAdapter {
699
+ create(entityType: string, data: EntityRow): Promise<{
700
+ id: string;
701
+ }>;
702
+ update(entityType: string, id: string, data: EntityRow): Promise<void>;
703
+ delete(entityType: string, id: string): Promise<void>;
704
+ getById(entityType: string, id: string): Promise<EntityRow | null>;
705
+ list(entityType: string): Promise<EntityRow[]>;
706
+ }
707
+ /**
708
+ * Simple in-memory persistence for dev/testing and offline previews.
709
+ * Keys each entity collection by type, rows by generated string id.
710
+ */
711
+ declare class InMemoryPersistence implements PersistenceAdapter {
712
+ private data;
713
+ private idCounter;
714
+ /**
715
+ * Seed the store with pre-existing rows.
716
+ *
717
+ * Accepts either a plain `Record<entityType, EntityRow[]>` or an iterable
718
+ * of `[entityType, EntityRow[]]` entries. Rows without an `id` get one
719
+ * generated at insert time; rows with an `id` keep it (so re-seeding
720
+ * after a schema rebuild preserves identities used in render bindings).
721
+ */
722
+ seed(seedData: Record<string, EntityRow[]> | Iterable<[string, EntityRow[]]>): void;
723
+ create(entityType: string, data: EntityRow): Promise<{
724
+ id: string;
725
+ }>;
726
+ update(entityType: string, id: string, data: EntityRow): Promise<void>;
727
+ delete(entityType: string, id: string): Promise<void>;
728
+ getById(entityType: string, id: string): Promise<EntityRow | null>;
729
+ list(entityType: string): Promise<EntityRow[]>;
730
+ /**
731
+ * Snapshot the entire store as a plain object (entityType → rows).
732
+ * Useful for feeding a fresh render-time binding layer with the
733
+ * current persistence view.
734
+ */
735
+ snapshot(): Record<string, EntityRow[]>;
736
+ }
737
+
674
738
  /**
675
739
  * OrbitalServerRuntime - Dynamic Server-Side Orbital Execution
676
740
  *
@@ -867,18 +931,7 @@ interface OrbitalServerRuntimeConfig {
867
931
  */
868
932
  contextExtensions?: EvaluationContextExtensions;
869
933
  }
870
- /**
871
- * Adapter for persisting entity data
872
- */
873
- interface PersistenceAdapter {
874
- create(entityType: string, data: EntityRow): Promise<{
875
- id: string;
876
- }>;
877
- update(entityType: string, id: string, data: EntityRow): Promise<void>;
878
- delete(entityType: string, id: string): Promise<void>;
879
- getById(entityType: string, id: string): Promise<EntityRow | null>;
880
- list(entityType: string): Promise<Array<EntityRow>>;
881
- }
934
+
882
935
  declare class OrbitalServerRuntime {
883
936
  protected orbitals: Map<string, RegisteredOrbital>;
884
937
  private eventBus;
@@ -1116,4 +1169,4 @@ declare class OrbitalServerRuntime {
1116
1169
  */
1117
1170
  declare function createOrbitalServerRuntime(config?: OrbitalServerRuntimeConfig): OrbitalServerRuntime;
1118
1171
 
1119
- export { processEvent as A, type EffectResult as B, type LoaderConfig as C, LocalPersistenceAdapter as D, type EntitySharingMap as E, OrbitalServerRuntime as F, type RuntimeTraitTick as G, createOrbitalServerRuntime as H, type ImportChainLike as I, type LoadResult as L, type OrbitalEventRequest as O, type PersistenceAdapter as P, type RegisteredOrbital as R, type SchemaLoader as S, type UnifiedLoaderOptions as U, type LoadedSchema as a, type LoadedOrbital as b, EventBus as c, type EventNamespaceMap as d, type OrbitalEventResponse as e, type OrbitalServerRuntimeConfig as f, type PreprocessOptions as g, type PreprocessResult as h, type PreprocessedSchema as i, type ProcessEventOptions as j, type RuntimeOrbital as k, type RuntimeOrbitalSchema as l, type RuntimeTrait as m, StateMachineManager as n, createInitialTraitState as o, findInitialState as p, findTransition as q, getIsolatedCollectionName as r, getNamespacedEvent as s, isBrowser as t, isElectron as u, isNamespacedEvent as v, isNode as w, normalizeEventKey as x, parseNamespacedEvent as y, preprocessSchema as z };
1172
+ export { preprocessSchema as A, processEvent as B, type EffectResult as C, type LoaderConfig as D, type EntitySharingMap as E, LocalPersistenceAdapter as F, OrbitalServerRuntime as G, type RuntimeTraitTick as H, type ImportChainLike as I, createOrbitalServerRuntime as J, type LoadResult as L, type OrbitalEventRequest as O, type PersistenceAdapter as P, type RegisteredOrbital as R, type SchemaLoader as S, type UnifiedLoaderOptions as U, type LoadedSchema as a, type LoadedOrbital as b, EventBus as c, type EventNamespaceMap as d, InMemoryPersistence as e, type OrbitalEventResponse as f, type OrbitalServerRuntimeConfig as g, type PreprocessOptions as h, type PreprocessResult as i, type PreprocessedSchema as j, type ProcessEventOptions as k, type RuntimeOrbital as l, type RuntimeOrbitalSchema as m, type RuntimeTrait as n, StateMachineManager as o, createInitialTraitState as p, findInitialState as q, findTransition as r, getIsolatedCollectionName as s, getNamespacedEvent as t, isBrowser as u, isElectron as v, isNamespacedEvent as w, isNode as x, normalizeEventKey as y, parseNamespacedEvent as z };
@@ -1,4 +1,4 @@
1
1
  import 'express';
2
- export { B as EffectResult, C as LoaderConfig, D as LocalPersistenceAdapter, O as OrbitalEventRequest, e as OrbitalEventResponse, F as OrbitalServerRuntime, f as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, k as RuntimeOrbital, l as RuntimeOrbitalSchema, m as RuntimeTrait, G as RuntimeTraitTick, H as createOrbitalServerRuntime } from './OrbitalServerRuntime-B5lfym6T.js';
2
+ export { C as EffectResult, e as InMemoryPersistence, D as LoaderConfig, F as LocalPersistenceAdapter, O as OrbitalEventRequest, f as OrbitalEventResponse, G as OrbitalServerRuntime, g as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, l as RuntimeOrbital, m as RuntimeOrbitalSchema, n as RuntimeTrait, H as RuntimeTraitTick, J as createOrbitalServerRuntime } from './OrbitalServerRuntime-D-b_dg8I.js';
3
3
  import './types-DwDhc9Jt.js';
4
4
  import '@almadar/core';
@@ -1,4 +1,5 @@
1
- import { EventBus, createUnifiedLoader, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-K36736GF.js';
1
+ import { EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, StateMachineManager, createContextFromBindings, EffectExecutor } from './chunk-RCWMQH7Y.js';
2
+ export { InMemoryPersistence } from './chunk-RCWMQH7Y.js';
2
3
  import './chunk-PZ5AY32C.js';
3
4
  import { Router } from 'express';
4
5
  import * as fs from 'fs';
@@ -6,7 +7,6 @@ import fs__default from 'fs';
6
7
  import path from 'path';
7
8
  import { evaluateGuard, evaluate } from '@almadar/evaluator';
8
9
  import { isInlineTrait, isEntityCall } from '@almadar/core';
9
- import { faker } from '@faker-js/faker';
10
10
  import * as net from 'net';
11
11
  import { execSync } from 'child_process';
12
12
 
@@ -81,283 +81,6 @@ var LocalPersistenceAdapter = class {
81
81
  }
82
82
  }
83
83
  };
84
- var MockPersistenceAdapter = class {
85
- stores = /* @__PURE__ */ new Map();
86
- schemas = /* @__PURE__ */ new Map();
87
- idCounters = /* @__PURE__ */ new Map();
88
- config;
89
- constructor(config = {}) {
90
- this.config = {
91
- defaultSeedCount: 6,
92
- debug: false,
93
- ...config
94
- };
95
- if (config.seed !== void 0) {
96
- faker.seed(config.seed);
97
- if (this.config.debug) {
98
- console.log(`[MockPersistence] Using seed: ${config.seed}`);
99
- }
100
- }
101
- }
102
- // ============================================================================
103
- // Store Management
104
- // ============================================================================
105
- getStore(entityName) {
106
- const normalized = entityName.toLowerCase();
107
- if (!this.stores.has(normalized)) {
108
- this.stores.set(normalized, /* @__PURE__ */ new Map());
109
- this.idCounters.set(normalized, 0);
110
- }
111
- return this.stores.get(normalized);
112
- }
113
- nextId(entityName) {
114
- const normalized = entityName.toLowerCase();
115
- const counter = (this.idCounters.get(normalized) ?? 0) + 1;
116
- this.idCounters.set(normalized, counter);
117
- return `${this.capitalizeFirst(entityName)} Id ${counter}`;
118
- }
119
- // ============================================================================
120
- // Schema & Seeding
121
- // ============================================================================
122
- /**
123
- * Register an entity schema and seed mock data.
124
- * If the schema has seedData, those instances are used directly.
125
- * Otherwise, random mock data is generated with faker.
126
- */
127
- registerEntity(schema, seedCount) {
128
- const normalized = schema.name.toLowerCase();
129
- this.schemas.set(normalized, schema);
130
- if (schema.seedData && schema.seedData.length > 0) {
131
- this.seedFromInstances(schema.name, schema.seedData);
132
- } else {
133
- const count = seedCount ?? this.config.defaultSeedCount ?? 6;
134
- this.seed(schema.name, schema.fields, count);
135
- }
136
- }
137
- /**
138
- * Seed an entity with pre-authored instance data.
139
- */
140
- seedFromInstances(entityName, instances) {
141
- const store = this.getStore(entityName);
142
- if (this.config.debug) {
143
- console.log(`[MockPersistence] Seeding ${instances.length} ${entityName} from schema instances...`);
144
- }
145
- for (const instance of instances) {
146
- const id = instance.id || this.nextId(entityName);
147
- const now = (/* @__PURE__ */ new Date()).toISOString();
148
- const item = {
149
- ...instance,
150
- id,
151
- createdAt: instance.createdAt || now,
152
- updatedAt: now
153
- };
154
- store.set(id, item);
155
- }
156
- }
157
- /**
158
- * Seed an entity with mock data.
159
- */
160
- seed(entityName, fields, count) {
161
- const store = this.getStore(entityName);
162
- const normalized = entityName.toLowerCase();
163
- if (this.config.debug) {
164
- console.log(`[MockPersistence] Seeding ${count} ${entityName}...`);
165
- }
166
- for (let i = 0; i < count; i++) {
167
- const item = this.generateMockItem(normalized, entityName, fields, i + 1);
168
- store.set(item.id, item);
169
- }
170
- }
171
- /**
172
- * Generate a single mock item based on field schemas.
173
- */
174
- generateMockItem(normalizedName, entityName, fields, index) {
175
- const id = this.nextId(entityName);
176
- const now = (/* @__PURE__ */ new Date()).toISOString();
177
- const item = {
178
- id,
179
- createdAt: faker.date.past({ years: 1 }).toISOString(),
180
- updatedAt: now
181
- };
182
- for (const field of fields) {
183
- if (field.name === "id" || field.name === "createdAt" || field.name === "updatedAt") {
184
- continue;
185
- }
186
- item[field.name] = this.generateFieldValue(entityName, field, index);
187
- }
188
- return item;
189
- }
190
- /**
191
- * Generate a mock value for a field based on its schema.
192
- */
193
- generateFieldValue(entityName, field, index) {
194
- if (field.default !== void 0) {
195
- if (field.default === "@now") {
196
- return (/* @__PURE__ */ new Date()).toISOString();
197
- }
198
- return field.default;
199
- }
200
- const fieldType = field.type.toLowerCase();
201
- switch (fieldType) {
202
- case "string":
203
- return this.generateStringValue(entityName, field, index);
204
- case "number":
205
- return faker.number.int({ min: 0, max: 100 });
206
- case "boolean":
207
- return faker.datatype.boolean();
208
- case "date":
209
- case "timestamp":
210
- case "datetime":
211
- return this.generateDateValue(field);
212
- case "enum":
213
- if (field.values && field.values.length > 0) {
214
- return faker.helpers.arrayElement(field.values);
215
- }
216
- return null;
217
- case "relation":
218
- return null;
219
- // Relations need special handling
220
- case "array":
221
- return [];
222
- case "object":
223
- return null;
224
- default:
225
- return this.generateStringValue(entityName, field, index);
226
- }
227
- }
228
- /**
229
- * Generate a string value based on the field's declared schema metadata.
230
- * Reads `values` (enum) first, then `format` (email/url/phone/uuid/date/
231
- * datetime), then falls back to faker.lorem.words. No field-name heuristics
232
- * — the schema is the source of truth. If a caller needs a real email, they
233
- * declare `format: "email"`; if they need an enum, they declare `values: [...]`.
234
- */
235
- generateStringValue(_entityName, field, _index) {
236
- if (field.values && field.values.length > 0) {
237
- return faker.helpers.arrayElement(field.values);
238
- }
239
- switch (field.format) {
240
- case "email":
241
- return faker.internet.email();
242
- case "url":
243
- return faker.internet.url();
244
- case "phone":
245
- return faker.phone.number();
246
- case "uuid":
247
- return faker.string.uuid();
248
- case "date":
249
- return faker.date.recent().toISOString().split("T")[0];
250
- case "datetime":
251
- return faker.date.recent().toISOString();
252
- default:
253
- return faker.lorem.words(2);
254
- }
255
- }
256
- /**
257
- * Generate a date value. Uses the field's `format` (date vs datetime) to
258
- * decide ISO shape; otherwise returns a recent ISO-8601 datetime. No
259
- * field-name heuristics.
260
- */
261
- generateDateValue(field) {
262
- const date = faker.date.recent({ days: 30 });
263
- if (field.format === "date") return date.toISOString().split("T")[0];
264
- return date.toISOString();
265
- }
266
- capitalizeFirst(str) {
267
- return str.charAt(0).toUpperCase() + str.slice(1);
268
- }
269
- // ============================================================================
270
- // PersistenceAdapter Implementation
271
- // ============================================================================
272
- async create(entityType, data) {
273
- const store = this.getStore(entityType);
274
- const id = this.nextId(entityType);
275
- const now = (/* @__PURE__ */ new Date()).toISOString();
276
- const withDefaults = this.applyFieldDefaults(entityType, data);
277
- const item = {
278
- ...withDefaults,
279
- id,
280
- createdAt: now,
281
- updatedAt: now
282
- };
283
- store.set(id, item);
284
- return { id };
285
- }
286
- /**
287
- * Fill in any entity-declared field defaults that the caller omitted.
288
- * SAVE payloads coming from form-section only carry the fields the user
289
- * edited; persisted rows should still honor `field.default` so downstream
290
- * row-content probes (VG11f) see a row whose every declared-default field
291
- * is non-empty. `@now` resolves to the current ISO timestamp.
292
- */
293
- applyFieldDefaults(entityType, data) {
294
- const schema = this.schemas.get(entityType.toLowerCase());
295
- if (!schema) return data;
296
- const result = { ...data };
297
- for (const field of schema.fields) {
298
- if (field.name === "id" || field.name === "createdAt" || field.name === "updatedAt") continue;
299
- if (result[field.name] !== void 0) continue;
300
- if (field.default === void 0) continue;
301
- result[field.name] = field.default === "@now" ? (/* @__PURE__ */ new Date()).toISOString() : field.default;
302
- }
303
- return result;
304
- }
305
- async update(entityType, id, data) {
306
- const store = this.getStore(entityType);
307
- const existing = store.get(id);
308
- if (!existing) {
309
- throw new Error(`Entity ${entityType} with id ${id} not found`);
310
- }
311
- const updated = {
312
- ...existing,
313
- ...data,
314
- id,
315
- // Preserve original ID
316
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
317
- };
318
- store.set(id, updated);
319
- }
320
- async delete(entityType, id) {
321
- const store = this.getStore(entityType);
322
- if (!store.has(id)) {
323
- throw new Error(`Entity ${entityType} with id ${id} not found`);
324
- }
325
- store.delete(id);
326
- }
327
- async getById(entityType, id) {
328
- const store = this.getStore(entityType);
329
- return store.get(id) ?? null;
330
- }
331
- async list(entityType) {
332
- const store = this.getStore(entityType);
333
- return Array.from(store.values());
334
- }
335
- // ============================================================================
336
- // Utilities
337
- // ============================================================================
338
- /**
339
- * Clear all data for an entity.
340
- */
341
- clear(entityName) {
342
- const normalized = entityName.toLowerCase();
343
- this.stores.delete(normalized);
344
- this.idCounters.delete(normalized);
345
- }
346
- /**
347
- * Clear all data.
348
- */
349
- clearAll() {
350
- this.stores.clear();
351
- this.idCounters.clear();
352
- }
353
- /**
354
- * Get count of items for an entity.
355
- */
356
- count(entityName) {
357
- const store = this.getStore(entityName);
358
- return store.size;
359
- }
360
- };
361
84
  function globToRegex(glob) {
362
85
  let regex = "";
363
86
  let i = 0;
@@ -636,35 +359,6 @@ function createOsHandlers(ctx) {
636
359
  }
637
360
 
638
361
  // src/OrbitalServerRuntime.ts
639
- var InMemoryPersistence = class {
640
- data = /* @__PURE__ */ new Map();
641
- idCounter = 0;
642
- async create(entityType, data) {
643
- const id = data.id || `${entityType}-${++this.idCounter}`;
644
- if (!this.data.has(entityType)) {
645
- this.data.set(entityType, /* @__PURE__ */ new Map());
646
- }
647
- this.data.get(entityType).set(id, { ...data, id });
648
- return { id };
649
- }
650
- async update(entityType, id, data) {
651
- const collection = this.data.get(entityType);
652
- if (collection?.has(id)) {
653
- const existing = collection.get(id);
654
- collection.set(id, { ...existing, ...data });
655
- }
656
- }
657
- async delete(entityType, id) {
658
- this.data.get(entityType)?.delete(id);
659
- }
660
- async getById(entityType, id) {
661
- return this.data.get(entityType)?.get(id) || null;
662
- }
663
- async list(entityType) {
664
- const collection = this.data.get(entityType);
665
- return collection ? Array.from(collection.values()) : [];
666
- }
667
- };
668
362
  function needsPreprocessing(schema) {
669
363
  for (const orbital of schema.orbitals) {
670
364
  const uses = orbital.uses;