@almadar/core 2.5.0 → 2.5.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.
@@ -1,4 +1,5 @@
1
- import { T as TraitEventContract, E as EntityField, a as EntityPersistence, O as OrbitalDefinition, b as OrbitalSchema, c as Trait, d as Entity, P as Page } from './schema-BrFR8WMF.js';
1
+ import { T as TraitEventContract, E as EntityField, a as EntityPersistence, O as OrbitalDefinition, b as OrbitalSchema, c as Trait, d as Entity, P as Page } from './schema-DmrHbrqM.js';
2
+ export { C as ComposeBehaviorsInput, a as ComposeBehaviorsResult, E as EventWiringEntry, L as LayoutStrategy, b as applyEventWiring, c as composeBehaviors, d as detectLayoutStrategy } from './compose-behaviors-ClEyviUI.js';
2
3
  import 'zod';
3
4
  import '@almadar/patterns';
4
5
 
package/dist/builders.js CHANGED
@@ -1,3 +1,416 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/builders/layout-strategy.ts
4
+ function hasSequentialChain(wiring) {
5
+ if (wiring.length < 2) {
6
+ return false;
7
+ }
8
+ const adjacency = /* @__PURE__ */ new Map();
9
+ const allTargets = /* @__PURE__ */ new Set();
10
+ for (const entry of wiring) {
11
+ let targets = adjacency.get(entry.from);
12
+ if (!targets) {
13
+ targets = /* @__PURE__ */ new Set();
14
+ adjacency.set(entry.from, targets);
15
+ }
16
+ targets.add(entry.to);
17
+ allTargets.add(entry.to);
18
+ }
19
+ const roots = [];
20
+ for (const from of adjacency.keys()) {
21
+ if (!allTargets.has(from)) {
22
+ roots.push(from);
23
+ }
24
+ }
25
+ const starts = roots.length > 0 ? roots : [...adjacency.keys()];
26
+ for (const start of starts) {
27
+ let current = start;
28
+ let length = 1;
29
+ const visited = /* @__PURE__ */ new Set([current]);
30
+ while (true) {
31
+ const nexts = adjacency.get(current);
32
+ if (!nexts || nexts.size === 0) break;
33
+ let advanced = false;
34
+ for (const next of nexts) {
35
+ if (!visited.has(next)) {
36
+ visited.add(next);
37
+ current = next;
38
+ length++;
39
+ advanced = true;
40
+ break;
41
+ }
42
+ }
43
+ if (!advanced) break;
44
+ }
45
+ if (length >= 3) {
46
+ return true;
47
+ }
48
+ }
49
+ return false;
50
+ }
51
+ function detectLayoutStrategy(orbitals, eventWiring) {
52
+ if (eventWiring && eventWiring.length > 0 && hasSequentialChain(eventWiring)) {
53
+ return "wizard-flow";
54
+ }
55
+ const count = orbitals.length;
56
+ if (count <= 1) {
57
+ return "single";
58
+ }
59
+ if (count <= 4) {
60
+ return "tabs";
61
+ }
62
+ return "sidebar";
63
+ }
64
+ var UI_SLOTS = [
65
+ // App slots
66
+ "main",
67
+ "sidebar",
68
+ "modal",
69
+ "drawer",
70
+ "overlay",
71
+ "center",
72
+ "toast",
73
+ "floating",
74
+ "system",
75
+ // For invisible system components (InputListener, CollisionDetector)
76
+ "content",
77
+ "screen",
78
+ // Game HUD slots
79
+ "hud",
80
+ "hud-top",
81
+ "hud-bottom",
82
+ "hud.health",
83
+ "hud.score",
84
+ "hud.inventory",
85
+ "hud.stamina",
86
+ // Game overlay slots
87
+ "overlay.inventory",
88
+ "overlay.dialogue",
89
+ "overlay.menu",
90
+ "overlay.pause"
91
+ ];
92
+ z.enum(UI_SLOTS);
93
+ var EffectSchema = z.array(z.unknown()).min(1).refine(
94
+ (arr) => typeof arr[0] === "string",
95
+ { message: "Effect must be an S-expression with a string operator as first element" }
96
+ );
97
+ var SExprAtomSchema = z.union([
98
+ z.string(),
99
+ z.number(),
100
+ z.boolean(),
101
+ z.null(),
102
+ z.record(z.unknown())
103
+ // Objects for payload data
104
+ ]);
105
+ var SExprSchema = z.lazy(
106
+ () => z.union([
107
+ SExprAtomSchema,
108
+ z.array(z.lazy(() => SExprSchema)).min(1).refine(
109
+ (arr) => typeof arr[0] === "string",
110
+ { message: "S-expression array must have a string operator as first element" }
111
+ )
112
+ ])
113
+ );
114
+ var ExpressionSchema = SExprSchema;
115
+
116
+ // src/types/state-machine.ts
117
+ var StateSchema = z.object({
118
+ name: z.string().min(1, "State name is required"),
119
+ isInitial: z.boolean().optional(),
120
+ isTerminal: z.boolean().optional(),
121
+ isFinal: z.boolean().optional(),
122
+ description: z.string().optional(),
123
+ onEntry: z.array(z.string()).optional(),
124
+ onExit: z.array(z.string()).optional()
125
+ });
126
+ var PayloadFieldSchema = z.object({
127
+ name: z.string().min(1),
128
+ type: z.enum(["string", "number", "boolean", "object", "array"]),
129
+ required: z.boolean().optional()
130
+ });
131
+ var EventSchema = z.object({
132
+ key: z.string().min(1, "Event key is required"),
133
+ name: z.string().min(1, "Event name is required"),
134
+ description: z.string().optional(),
135
+ payloadSchema: z.array(PayloadFieldSchema).optional(),
136
+ classification: z.enum(["domain", "system"]).optional(),
137
+ semanticRole: z.string().optional()
138
+ });
139
+ var GuardSchema = z.object({
140
+ name: z.string().min(1, "Guard name is required"),
141
+ expression: ExpressionSchema,
142
+ description: z.string().optional()
143
+ });
144
+ var TransitionSchema = z.object({
145
+ from: z.string().min(1, "Transition source state is required"),
146
+ to: z.string().min(1, "Transition target state is required"),
147
+ event: z.string().min(1, "Transition event is required"),
148
+ guard: ExpressionSchema.nullish(),
149
+ effects: z.array(EffectSchema).optional(),
150
+ description: z.string().nullish()
151
+ });
152
+ var StateMachineSchema = z.object({
153
+ states: z.array(StateSchema).min(1, "At least one state is required"),
154
+ events: z.array(EventSchema),
155
+ transitions: z.array(TransitionSchema),
156
+ guards: z.array(GuardSchema).optional()
157
+ });
158
+
159
+ // src/types/trait.ts
160
+ var TraitCategorySchema = z.enum([
161
+ "lifecycle",
162
+ "temporal",
163
+ "validation",
164
+ "notification",
165
+ "integration",
166
+ "interaction",
167
+ "game-core",
168
+ "game-character",
169
+ "game-ai",
170
+ "game-combat",
171
+ "game-items",
172
+ "game-cards",
173
+ "game-board",
174
+ "game-puzzle"
175
+ ]);
176
+ var TraitEntityFieldSchema = z.object({
177
+ name: z.string().min(1),
178
+ type: z.enum([
179
+ "string",
180
+ "number",
181
+ "boolean",
182
+ "date",
183
+ "array",
184
+ "object",
185
+ "timestamp",
186
+ "datetime",
187
+ "enum"
188
+ ]),
189
+ required: z.boolean().optional(),
190
+ default: z.unknown().optional(),
191
+ values: z.array(z.string()).optional()
192
+ });
193
+ var TraitDataEntitySchema = z.object({
194
+ name: z.string().min(1),
195
+ collection: z.string().optional(),
196
+ fields: z.array(TraitEntityFieldSchema).min(1),
197
+ timestamps: z.boolean().optional(),
198
+ description: z.string().optional(),
199
+ runtime: z.boolean().optional(),
200
+ singleton: z.boolean().optional(),
201
+ pages: z.array(z.string()).optional()
202
+ });
203
+ var TraitTickSchema = z.object({
204
+ name: z.string().min(1),
205
+ description: z.string().optional(),
206
+ priority: z.number().optional(),
207
+ interval: z.union([z.literal("frame"), z.number().positive()]),
208
+ appliesTo: z.array(z.string()).optional(),
209
+ pages: z.array(z.string()).optional(),
210
+ guard: ExpressionSchema.optional(),
211
+ effects: z.array(EffectSchema).min(1),
212
+ emits: z.array(z.string()).optional()
213
+ });
214
+ var EventScopeSchema = z.enum(["internal", "external"]);
215
+ var EventPayloadFieldSchema = z.object({
216
+ name: z.string().min(1),
217
+ type: z.enum(["string", "number", "boolean", "object", "array", "entity"]),
218
+ required: z.boolean().optional(),
219
+ description: z.string().optional(),
220
+ entityType: z.string().optional()
221
+ });
222
+ var TraitEventContractSchema = z.object({
223
+ event: z.string().min(1).regex(
224
+ /^[A-Z][A-Z0-9_]*$/,
225
+ "Event name must be UPPER_SNAKE_CASE"
226
+ ),
227
+ description: z.string().optional(),
228
+ payload: z.array(EventPayloadFieldSchema).optional(),
229
+ scope: EventScopeSchema.optional()
230
+ });
231
+ var TraitEventListenerSchema = z.object({
232
+ event: z.string().min(1),
233
+ triggers: z.string().min(1),
234
+ guard: ExpressionSchema.optional(),
235
+ scope: EventScopeSchema.optional(),
236
+ payloadMapping: z.record(z.string()).optional()
237
+ });
238
+ var RequiredFieldSchema = z.object({
239
+ name: z.string().min(1),
240
+ type: z.enum(["string", "number", "boolean", "date", "array", "object", "timestamp", "datetime", "enum"]),
241
+ description: z.string().optional()
242
+ });
243
+ z.object({
244
+ ref: z.string().min(1),
245
+ linkedEntity: z.string().optional(),
246
+ config: z.record(z.record(z.unknown())).optional(),
247
+ appliesTo: z.array(z.string()).optional()
248
+ });
249
+ var TraitSchema = z.object({
250
+ name: z.string().min(1),
251
+ description: z.string().optional(),
252
+ description_visual_prompt: z.string().optional(),
253
+ category: TraitCategorySchema.optional(),
254
+ linkedEntity: z.string().optional(),
255
+ requiredFields: z.array(RequiredFieldSchema).optional(),
256
+ dataEntities: z.array(TraitDataEntitySchema).optional(),
257
+ stateMachine: StateMachineSchema.optional(),
258
+ initialEffects: z.array(EffectSchema).optional(),
259
+ ticks: z.array(TraitTickSchema).optional(),
260
+ emits: z.array(TraitEventContractSchema).optional(),
261
+ listens: z.array(TraitEventListenerSchema).optional(),
262
+ ui: z.record(z.unknown()).optional()
263
+ });
264
+ z.union([
265
+ z.string().min(1),
266
+ z.object({
267
+ ref: z.string().min(1),
268
+ config: z.record(z.unknown()).optional(),
269
+ linkedEntity: z.string().optional()
270
+ }),
271
+ TraitSchema
272
+ // Allow inline trait definitions
273
+ ]);
274
+ function isInlineTrait(traitRef) {
275
+ return typeof traitRef === "object" && "name" in traitRef && !("ref" in traitRef);
276
+ }
277
+
278
+ // src/builders/event-wiring.ts
279
+ function findInlineTrait(orbitals, name) {
280
+ for (const orbital of orbitals) {
281
+ for (const traitRef of orbital.traits) {
282
+ if (isInlineTrait(traitRef) && traitRef.name === name) {
283
+ return traitRef;
284
+ }
285
+ }
286
+ }
287
+ return null;
288
+ }
289
+ function hasEmit(emits, event) {
290
+ return emits.some((e) => e.event === event);
291
+ }
292
+ function hasListen(listens, event, triggers) {
293
+ return listens.some((l) => l.event === event && l.triggers === triggers);
294
+ }
295
+ function applyEventWiring(orbitals, wiring) {
296
+ const cloned = JSON.parse(
297
+ JSON.stringify(orbitals)
298
+ );
299
+ for (const entry of wiring) {
300
+ const sourceTrait = findInlineTrait(cloned, entry.from);
301
+ if (sourceTrait) {
302
+ if (!sourceTrait.emits) {
303
+ sourceTrait.emits = [];
304
+ }
305
+ if (!hasEmit(sourceTrait.emits, entry.event)) {
306
+ sourceTrait.emits.push({
307
+ event: entry.event,
308
+ scope: "external"
309
+ });
310
+ }
311
+ }
312
+ const targetTrait = findInlineTrait(cloned, entry.to);
313
+ if (targetTrait) {
314
+ if (!targetTrait.listens) {
315
+ targetTrait.listens = [];
316
+ }
317
+ if (!hasListen(targetTrait.listens, entry.event, entry.triggers)) {
318
+ targetTrait.listens.push({
319
+ event: entry.event,
320
+ triggers: entry.triggers,
321
+ scope: "external"
322
+ });
323
+ }
324
+ }
325
+ }
326
+ return cloned;
327
+ }
328
+
329
+ // src/builders/compose-behaviors.ts
330
+ function toKebabCase(name) {
331
+ return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
332
+ }
333
+ function generatePages(orbitals, strategy) {
334
+ switch (strategy) {
335
+ case "single": {
336
+ const orbital = orbitals[0];
337
+ const name = orbital?.name ?? "Main";
338
+ return [
339
+ {
340
+ name: `${name}Page`,
341
+ path: "/",
342
+ isInitial: true,
343
+ primaryEntity: getEntityName(orbital)
344
+ }
345
+ ];
346
+ }
347
+ case "dashboard": {
348
+ return [
349
+ {
350
+ name: "DashboardPage",
351
+ path: "/",
352
+ viewType: "dashboard",
353
+ isInitial: true
354
+ }
355
+ ];
356
+ }
357
+ case "sidebar":
358
+ case "tabs":
359
+ case "wizard-flow": {
360
+ return orbitals.map((orbital, index) => ({
361
+ name: `${orbital.name}Page`,
362
+ path: index === 0 ? "/" : `/${toKebabCase(orbital.name)}`,
363
+ isInitial: index === 0,
364
+ primaryEntity: getEntityName(orbital)
365
+ }));
366
+ }
367
+ }
368
+ }
369
+ function getEntityName(orbital) {
370
+ if (!orbital) return void 0;
371
+ const entity = orbital.entity;
372
+ if (typeof entity === "string") {
373
+ return entity.replace(".entity", "");
374
+ }
375
+ return entity.name;
376
+ }
377
+ function composeBehaviors(input) {
378
+ const {
379
+ appName,
380
+ orbitals: rawOrbitals,
381
+ layoutStrategy: strategyInput,
382
+ eventWiring
383
+ } = input;
384
+ const wiredOrbitals = eventWiring && eventWiring.length > 0 ? applyEventWiring(rawOrbitals, eventWiring) : rawOrbitals;
385
+ const strategy = !strategyInput || strategyInput === "auto" ? detectLayoutStrategy(wiredOrbitals, eventWiring) : strategyInput;
386
+ const pages = generatePages(wiredOrbitals, strategy);
387
+ const orbitalsWithPages = wiredOrbitals.map((orbital, index) => {
388
+ if (orbital.pages && orbital.pages.length > 0) {
389
+ return orbital;
390
+ }
391
+ const page = strategy === "dashboard" || strategy === "single" ? pages[0] : pages[index];
392
+ return {
393
+ ...orbital,
394
+ pages: page ? [page] : []
395
+ };
396
+ });
397
+ const schema = {
398
+ name: appName,
399
+ version: "1.0.0",
400
+ orbitals: orbitalsWithPages
401
+ };
402
+ return {
403
+ schema,
404
+ layout: {
405
+ strategy,
406
+ pageCount: pages.length
407
+ },
408
+ wiring: {
409
+ connections: eventWiring?.length ?? 0
410
+ }
411
+ };
412
+ }
413
+
1
414
  // src/builders.ts
2
415
  function ensureIdField(fields) {
3
416
  if (fields.some((f) => f.name === "id")) return fields;
@@ -157,6 +570,6 @@ function pipe(orbitals, events, appName) {
157
570
  };
158
571
  }
159
572
 
160
- export { compose, connect, ensureIdField, extractTrait, makeEntity, makeOrbital, makePage, mergeOrbitals, pipe, plural, wire };
573
+ export { applyEventWiring, compose, composeBehaviors, connect, detectLayoutStrategy, ensureIdField, extractTrait, makeEntity, makeOrbital, makePage, mergeOrbitals, pipe, plural, wire };
161
574
  //# sourceMappingURL=builders.js.map
162
575
  //# sourceMappingURL=builders.js.map