@almadar/core 2.9.0 → 2.10.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.
@@ -1,4 +1,4 @@
1
- import { O as OrbitalDefinition, b as OrbitalSchema } from './schema-BNBpNxGb.js';
1
+ import { O as OrbitalDefinition, b as OrbitalSchema, a as EntityPersistence } from './schema-B2HUtPrQ.js';
2
2
 
3
3
  /**
4
4
  * Event Wiring
@@ -116,4 +116,46 @@ interface ComposeBehaviorsResult {
116
116
  */
117
117
  declare function composeBehaviors(input: ComposeBehaviorsInput): ComposeBehaviorsResult;
118
118
 
119
- export { type ComposeBehaviorsInput as C, type EventWiringEntry as E, type LayoutStrategy as L, type ComposeBehaviorsResult as a, applyEventWiring as b, composeBehaviors as c, detectLayoutStrategy as d };
119
+ /**
120
+ * Adapt Behavior Builder
121
+ *
122
+ * Deterministically rebinds a behavior's entity, fields, traits, and pages
123
+ * to a target entity. No LLM involved. Render-UI trees stay from the behavior.
124
+ *
125
+ * Uses core types (OrbitalDefinition, Entity, EntityField, Trait, Page)
126
+ * instead of untyped Record<string, unknown>.
127
+ *
128
+ * @packageDocumentation
129
+ */
130
+
131
+ interface AdaptBehaviorInput {
132
+ /** Source orbital definition (from a golden behavior) */
133
+ source: OrbitalDefinition;
134
+ /** Target entity name (PascalCase) */
135
+ entityName: string;
136
+ /** Target fields */
137
+ fields: Array<{
138
+ name: string;
139
+ type: string;
140
+ required?: boolean;
141
+ }>;
142
+ /** Target collection name (defaults to plural lowercase of entityName) */
143
+ collection?: string;
144
+ /** Persistence mode (defaults to "persistent") */
145
+ persistence?: EntityPersistence;
146
+ }
147
+ interface AdaptBehaviorResult {
148
+ /** The adapted orbital definition */
149
+ orbital: OrbitalDefinition;
150
+ }
151
+ /**
152
+ * Adapt a behavior orbital to a new entity.
153
+ *
154
+ * Rebinds: entity name, collection, fields, trait names, page names/paths,
155
+ * entity references in render-ui trees and effects.
156
+ *
157
+ * Preserves: state machine structure, transitions, render-ui trees, event wiring.
158
+ */
159
+ declare function adaptBehavior(input: AdaptBehaviorInput): AdaptBehaviorResult;
160
+
161
+ export { type AdaptBehaviorInput as A, type ComposeBehaviorsInput as C, type EventWiringEntry as E, type LayoutStrategy as L, type AdaptBehaviorResult as a, type ComposeBehaviorsResult as b, adaptBehavior as c, applyEventWiring as d, composeBehaviors as e, detectLayoutStrategy as f };
@@ -1,5 +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-BNBpNxGb.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-j53THIou.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-B2HUtPrQ.js';
2
+ export { A as AdaptBehaviorInput, a as AdaptBehaviorResult, C as ComposeBehaviorsInput, b as ComposeBehaviorsResult, E as EventWiringEntry, L as LayoutStrategy, c as adaptBehavior, d as applyEventWiring, e as composeBehaviors, f as detectLayoutStrategy } from './adapt-behavior-WWazE3lN.js';
3
3
  import 'zod';
4
4
  import '@almadar/patterns';
5
5
 
package/dist/builders.js CHANGED
@@ -411,6 +411,164 @@ function composeBehaviors(input) {
411
411
  };
412
412
  }
413
413
 
414
+ // src/builders/adapt-behavior.ts
415
+ var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
416
+ "string",
417
+ "number",
418
+ "boolean",
419
+ "date",
420
+ "timestamp",
421
+ "datetime",
422
+ "array",
423
+ "object",
424
+ "enum",
425
+ "relation"
426
+ ]);
427
+ function normalizeFieldType(type) {
428
+ if (type.endsWith("[]")) return "array";
429
+ const lower = type.toLowerCase();
430
+ if (VALID_FIELD_TYPES.has(lower)) return lower;
431
+ return "string";
432
+ }
433
+ function deriveTraitSuffix(originalEntityName, traits) {
434
+ if (!traits.length) return "Management";
435
+ const traitName = traits[0].name;
436
+ if (traitName.startsWith(originalEntityName)) {
437
+ const suffix = traitName.slice(originalEntityName.length);
438
+ if (suffix && suffix.toLowerCase() !== originalEntityName.toLowerCase()) {
439
+ return suffix;
440
+ }
441
+ }
442
+ const lower = originalEntityName.toLowerCase();
443
+ const traitLower = traitName.toLowerCase();
444
+ if (traitLower.startsWith(lower)) {
445
+ const suffix = traitName.slice(lower.length);
446
+ if (suffix && suffix.toLowerCase() !== originalEntityName.toLowerCase()) {
447
+ return suffix;
448
+ }
449
+ }
450
+ return "Management";
451
+ }
452
+ function replaceEntityRefs(obj, oldEntity, newEntity) {
453
+ if (typeof obj === "string") {
454
+ return obj === oldEntity ? newEntity : obj.replace(new RegExp(oldEntity, "g"), newEntity);
455
+ }
456
+ if (Array.isArray(obj)) {
457
+ return obj.map((item) => replaceEntityRefs(item, oldEntity, newEntity));
458
+ }
459
+ if (obj && typeof obj === "object") {
460
+ const result = {};
461
+ for (const [key, value] of Object.entries(obj)) {
462
+ result[key] = replaceEntityRefs(value, oldEntity, newEntity);
463
+ }
464
+ return result;
465
+ }
466
+ return obj;
467
+ }
468
+ function cleanStaleBindings(stateMachine, validFields) {
469
+ const sm = stateMachine;
470
+ const transitions = sm.transitions;
471
+ if (!transitions) return;
472
+ for (const transition of transitions) {
473
+ const effects = transition.effects;
474
+ if (!effects) continue;
475
+ transition.effects = effects.filter((effect) => {
476
+ if (!Array.isArray(effect)) return true;
477
+ return !hasStaleBinding(effect, validFields);
478
+ });
479
+ }
480
+ }
481
+ function hasStaleBinding(value, validFields) {
482
+ if (typeof value === "string") {
483
+ if (value.startsWith("@entity.")) {
484
+ const field = value.slice("@entity.".length).split(".")[0];
485
+ return !validFields.has(field);
486
+ }
487
+ return false;
488
+ }
489
+ if (Array.isArray(value)) {
490
+ return value.some((item) => hasStaleBinding(item, validFields));
491
+ }
492
+ if (value && typeof value === "object") {
493
+ return Object.values(value).some((v) => hasStaleBinding(v, validFields));
494
+ }
495
+ return false;
496
+ }
497
+ function adaptBehavior(input) {
498
+ const { source, entityName, fields, collection, persistence } = input;
499
+ const orbital = JSON.parse(JSON.stringify(source));
500
+ const originalEntity = typeof orbital.entity === "string" ? orbital.entity : orbital.entity.name;
501
+ const targetCollection = collection ?? entityName.toLowerCase() + "s";
502
+ orbital.entity = {
503
+ name: entityName,
504
+ collection: targetCollection,
505
+ persistence: persistence ?? "persistent",
506
+ fields: [
507
+ { name: "id", type: "string", required: true },
508
+ ...fields.map((f) => ({
509
+ name: f.name,
510
+ type: normalizeFieldType(f.type),
511
+ ...f.required ? { required: true } : {}
512
+ }))
513
+ ].filter((f, i, arr) => arr.findIndex((x) => x.name === f.name) === i)
514
+ // dedupe id
515
+ };
516
+ const inlineTraits = orbital.traits.filter((t) => isInlineTrait(t));
517
+ const firstTraitSuffix = deriveTraitSuffix(originalEntity, inlineTraits);
518
+ const traitNameMap = /* @__PURE__ */ new Map();
519
+ for (const trait of inlineTraits) {
520
+ const oldTraitName = trait.name;
521
+ let newTraitName;
522
+ if (trait.name.toLowerCase().startsWith(originalEntity.toLowerCase())) {
523
+ const suffix = trait.name.slice(originalEntity.length) || firstTraitSuffix;
524
+ newTraitName = entityName + suffix;
525
+ } else {
526
+ newTraitName = entityName + trait.name;
527
+ }
528
+ traitNameMap.set(oldTraitName, newTraitName);
529
+ trait.name = newTraitName;
530
+ trait.linkedEntity = entityName;
531
+ if (trait.stateMachine) {
532
+ trait.stateMachine = replaceEntityRefs(
533
+ trait.stateMachine,
534
+ originalEntity,
535
+ entityName
536
+ );
537
+ trait.stateMachine = replaceEntityRefs(
538
+ trait.stateMachine,
539
+ oldTraitName,
540
+ newTraitName
541
+ );
542
+ const newFieldNames = new Set(fields.map((f) => f.name));
543
+ newFieldNames.add("id");
544
+ cleanStaleBindings(trait.stateMachine, newFieldNames);
545
+ }
546
+ }
547
+ orbital.name = entityName + firstTraitSuffix;
548
+ if (orbital.pages) {
549
+ for (const page of orbital.pages) {
550
+ if (typeof page === "string") continue;
551
+ const p = page;
552
+ if (p.name) p.name = entityName + "Page";
553
+ if (p.path) p.path = "/" + targetCollection;
554
+ if (p.traits) {
555
+ for (const ref of p.traits) {
556
+ if (typeof ref === "object" && "ref" in ref) {
557
+ ref.ref = traitNameMap.get(ref.ref) ?? entityName + firstTraitSuffix;
558
+ }
559
+ }
560
+ }
561
+ }
562
+ }
563
+ if ("domainContext" in orbital) {
564
+ const ctx = orbital.domainContext;
565
+ if (ctx?.vocabulary && typeof ctx.vocabulary === "object") {
566
+ ctx.vocabulary.item = entityName.toLowerCase();
567
+ }
568
+ }
569
+ return { orbital };
570
+ }
571
+
414
572
  // src/builders.ts
415
573
  function ensureIdField(fields) {
416
574
  if (fields.some((f) => f.name === "id")) return fields;
@@ -570,6 +728,6 @@ function pipe(orbitals, events, appName) {
570
728
  };
571
729
  }
572
730
 
573
- export { applyEventWiring, compose, composeBehaviors, connect, detectLayoutStrategy, ensureIdField, extractTrait, makeEntity, makeOrbital, makePage, mergeOrbitals, pipe, plural, wire };
731
+ export { adaptBehavior, applyEventWiring, compose, composeBehaviors, connect, detectLayoutStrategy, ensureIdField, extractTrait, makeEntity, makeOrbital, makePage, mergeOrbitals, pipe, plural, wire };
574
732
  //# sourceMappingURL=builders.js.map
575
733
  //# sourceMappingURL=builders.js.map