@emeryld/rrroutes-contract 2.6.6 → 2.7.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.
package/dist/index.mjs CHANGED
@@ -42,9 +42,9 @@ function collectNestedFieldSuggestions(shape, prefix = []) {
42
42
  }
43
43
  return suggestions;
44
44
  }
45
- function compilePath(path, params) {
46
- if (!params) return path;
47
- const withParams = path.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {
45
+ function compilePath(path3, params) {
46
+ if (!params) return path3;
47
+ const withParams = path3.replace(/:([A-Za-z0-9_]+)/g, (_, k) => {
48
48
  const v = params[k];
49
49
  if (v === void 0 || v === null) throw new Error(`Missing param :${k}`);
50
50
  return String(v);
@@ -119,8 +119,8 @@ function buildLowProfileLeaf(leaf) {
119
119
  }
120
120
  };
121
121
  }
122
- var keyOf = (method, path, encodeSafe) => {
123
- const key = `${method.toUpperCase()} ${path}`;
122
+ var keyOf = (method, path3, encodeSafe) => {
123
+ const key = `${method.toUpperCase()} ${path3}`;
124
124
  return encodeSafe ? encodeURIComponent(key) : key;
125
125
  };
126
126
 
@@ -296,8 +296,8 @@ function joinPaths(parent, child) {
296
296
  if (!trimmedChild) return trimmedParent;
297
297
  return `${trimmedParent}/${trimmedChild}`;
298
298
  }
299
- function assertDynamicLayerUniqueness(path, dynamicLayerMap) {
300
- const segments = path.split("/").filter(Boolean);
299
+ function assertDynamicLayerUniqueness(path3, dynamicLayerMap) {
300
+ const segments = path3.split("/").filter(Boolean);
301
301
  if (segments.length === 0) return;
302
302
  for (let i = 0; i < segments.length; i++) {
303
303
  const segment = segments[i];
@@ -335,21 +335,493 @@ function finalize(leaves) {
335
335
  function defineSocketEvents(config, events) {
336
336
  return { config, events };
337
337
  }
338
+
339
+ // src/export/schemaIntrospection.ts
340
+ import * as z3 from "zod";
341
+ var serializableSchemaKinds = [
342
+ "object",
343
+ "string",
344
+ "number",
345
+ "boolean",
346
+ "bigint",
347
+ "date",
348
+ "array",
349
+ "enum",
350
+ "literal",
351
+ "union",
352
+ "record",
353
+ "tuple",
354
+ "unknown",
355
+ "any"
356
+ ];
357
+ var globalHandlers = {};
358
+ function registerSchemaIntrospectionHandler(kind, handler) {
359
+ globalHandlers[kind] = handler;
360
+ }
361
+ function clearSchemaIntrospectionHandlers() {
362
+ for (const key of Object.keys(globalHandlers)) {
363
+ delete globalHandlers[key];
364
+ }
365
+ }
366
+ function getDef(schema) {
367
+ if (!schema || typeof schema !== "object") return void 0;
368
+ const anySchema = schema;
369
+ return anySchema._zod?.def ?? anySchema._def;
370
+ }
371
+ function getDescription(schema) {
372
+ const anyZ = z3;
373
+ const registry = anyZ.globalRegistry?.get ? anyZ.globalRegistry.get(schema) : void 0;
374
+ if (registry && typeof registry.description === "string") {
375
+ return registry.description;
376
+ }
377
+ const def = getDef(schema);
378
+ if (def && typeof def.description === "string") {
379
+ return def.description;
380
+ }
381
+ return void 0;
382
+ }
383
+ function unwrap(schema) {
384
+ let current = schema;
385
+ let optional = false;
386
+ let nullable = false;
387
+ const ZodEffectsCtor = z3.ZodEffects;
388
+ while (true) {
389
+ if (ZodEffectsCtor && current instanceof ZodEffectsCtor) {
390
+ const def = getDef(current) || {};
391
+ const sourceType = typeof current.sourceType === "function" ? current.sourceType() : def.schema;
392
+ if (!sourceType) break;
393
+ current = sourceType;
394
+ continue;
395
+ }
396
+ if (current instanceof z3.ZodOptional) {
397
+ optional = true;
398
+ const def = getDef(current);
399
+ current = def && def.innerType || current;
400
+ continue;
401
+ }
402
+ if (current instanceof z3.ZodNullable) {
403
+ nullable = true;
404
+ const def = getDef(current);
405
+ current = def && def.innerType || current;
406
+ continue;
407
+ }
408
+ if (current instanceof z3.ZodDefault) {
409
+ const def = getDef(current);
410
+ current = def && def.innerType || current;
411
+ continue;
412
+ }
413
+ break;
414
+ }
415
+ return { base: current, optional, nullable };
416
+ }
417
+ function mergeProps(a, b) {
418
+ if (!a && !b) return void 0;
419
+ return { ...a ?? {}, ...b ?? {} };
420
+ }
421
+ function inferKind(schema) {
422
+ if (schema instanceof z3.ZodString) return "string";
423
+ if (schema instanceof z3.ZodNumber) return "number";
424
+ if (schema instanceof z3.ZodBoolean) return "boolean";
425
+ if (schema instanceof z3.ZodBigInt) return "bigint";
426
+ if (schema instanceof z3.ZodDate) return "date";
427
+ if (schema instanceof z3.ZodArray) return "array";
428
+ if (schema instanceof z3.ZodObject) return "object";
429
+ if (schema instanceof z3.ZodUnion) return "union";
430
+ if (schema instanceof z3.ZodLiteral) return "literal";
431
+ if (schema instanceof z3.ZodEnum) return "enum";
432
+ if (schema instanceof z3.ZodRecord) return "record";
433
+ if (schema instanceof z3.ZodTuple) return "tuple";
434
+ if (schema instanceof z3.ZodUnknown) return "unknown";
435
+ if (schema instanceof z3.ZodAny) return "any";
436
+ return "unknown";
437
+ }
438
+ var defaultHandlers = {
439
+ intersection({ base, def, node }, ctx) {
440
+ const left = def?.left;
441
+ const right = def?.right;
442
+ const leftNode = left ? ctx.introspect(left) : void 0;
443
+ const rightNode = right ? ctx.introspect(right) : void 0;
444
+ const leftIsObj = leftNode?.kind === "object" && !!leftNode.properties;
445
+ const rightIsObj = rightNode?.kind === "object" && !!rightNode.properties;
446
+ if (leftIsObj && rightIsObj) {
447
+ node.kind = "object";
448
+ node.description = node.description ?? leftNode.description ?? rightNode.description;
449
+ node.properties = mergeProps(leftNode.properties, rightNode.properties);
450
+ return node;
451
+ }
452
+ if (leftNode && rightNode) {
453
+ node.properties = {
454
+ left: leftNode,
455
+ right: rightNode
456
+ };
457
+ }
458
+ return node;
459
+ },
460
+ object({ base, def, node }, ctx) {
461
+ const rawShape = base.shape ?? (def && typeof def.shape === "function" ? def.shape() : def?.shape);
462
+ const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
463
+ const props = {};
464
+ for (const key of Object.keys(shape)) {
465
+ const child = shape[key];
466
+ const childNode = ctx.introspect(child);
467
+ if (childNode) props[key] = childNode;
468
+ }
469
+ node.properties = props;
470
+ return node;
471
+ },
472
+ array({ def, node }, ctx) {
473
+ const inner = def && def.element || def && def.type || void 0;
474
+ if (inner) {
475
+ node.element = ctx.introspect(inner);
476
+ }
477
+ return node;
478
+ },
479
+ union({ def, node }, ctx) {
480
+ const options = def && def.options || [];
481
+ node.union = options.map((opt) => ctx.introspect(opt)).filter(Boolean);
482
+ return node;
483
+ },
484
+ literal({ def, node }) {
485
+ if (def) {
486
+ if (Array.isArray(def.values)) {
487
+ node.literal = def.values.length === 1 ? def.values[0] : def.values.slice();
488
+ } else {
489
+ node.literal = def.value;
490
+ }
491
+ }
492
+ return node;
493
+ },
494
+ enum({ def, node }) {
495
+ if (def) {
496
+ if (Array.isArray(def.values)) {
497
+ node.enumValues = def.values.slice();
498
+ } else if (def.entries && typeof def.entries === "object") {
499
+ node.enumValues = Object.values(def.entries).map((v) => String(v));
500
+ }
501
+ }
502
+ return node;
503
+ }
504
+ };
505
+ function createSchemaIntrospector(options = {}) {
506
+ const handlers = {
507
+ ...defaultHandlers,
508
+ ...globalHandlers,
509
+ ...options.handlers ?? {}
510
+ };
511
+ const introspect = (schema) => {
512
+ if (!schema) return void 0;
513
+ const unwrapped = unwrap(schema);
514
+ const base = unwrapped.base;
515
+ const def = getDef(base);
516
+ const kind = base instanceof z3.ZodIntersection ? "intersection" : inferKind(base);
517
+ const node = {
518
+ kind: kind === "intersection" ? "unknown" : kind,
519
+ optional: unwrapped.optional || void 0,
520
+ nullable: unwrapped.nullable || void 0,
521
+ description: getDescription(base)
522
+ };
523
+ const handler = handlers[kind];
524
+ if (!handler) return node;
525
+ return handler(
526
+ {
527
+ schema,
528
+ base,
529
+ def,
530
+ kind,
531
+ optional: unwrapped.optional,
532
+ nullable: unwrapped.nullable,
533
+ node
534
+ },
535
+ {
536
+ zod: z3,
537
+ introspect,
538
+ getDef,
539
+ unwrap,
540
+ getDescription
541
+ }
542
+ );
543
+ };
544
+ return introspect;
545
+ }
546
+ function introspectSchema(schema, options = {}) {
547
+ const introspect = createSchemaIntrospector(options);
548
+ return introspect(schema);
549
+ }
550
+
551
+ // src/export/serializeLeafContract.ts
552
+ function serializeContractSchema(schema, options) {
553
+ return schema ? introspectSchema(routeSchemaParse(schema), options) : void 0;
554
+ }
555
+ function serializeBodyFiles(cfg) {
556
+ if (!Array.isArray(cfg.bodyFiles) || cfg.bodyFiles.length === 0) {
557
+ return void 0;
558
+ }
559
+ return cfg.bodyFiles.map(({ name, maxCount }) => ({ name, maxCount }));
560
+ }
561
+ function serializeLeafContract(leaf, options = {}) {
562
+ const cfg = leaf.cfg;
563
+ return {
564
+ key: `${leaf.method.toUpperCase()} ${leaf.path}`,
565
+ method: leaf.method,
566
+ path: leaf.path,
567
+ cfg: {
568
+ description: cfg.description,
569
+ summary: cfg.summary,
570
+ docsGroup: cfg.docsGroup,
571
+ tags: Array.isArray(cfg.tags) ? cfg.tags.slice() : void 0,
572
+ deprecated: cfg.deprecated,
573
+ stability: cfg.stability,
574
+ docsHidden: cfg.docsHidden,
575
+ docsMeta: cfg.docsMeta,
576
+ feed: cfg.feed,
577
+ bodyFiles: serializeBodyFiles(cfg),
578
+ schemas: {
579
+ body: serializeContractSchema(cfg.bodySchema, options),
580
+ query: serializeContractSchema(cfg.querySchema, options),
581
+ params: serializeContractSchema(cfg.paramsSchema, options),
582
+ output: serializeContractSchema(cfg.outputSchema, options),
583
+ outputMeta: serializeContractSchema(cfg.outputMetaSchema, options),
584
+ queryExtension: serializeContractSchema(cfg.queryExtensionSchema, options)
585
+ }
586
+ }
587
+ };
588
+ }
589
+ function serializeLeavesContract(leaves, options = {}) {
590
+ return leaves.map((leaf) => serializeLeafContract(leaf, options));
591
+ }
592
+
593
+ // src/export/flattenSchema.ts
594
+ function normalizeType(schema) {
595
+ switch (schema.kind) {
596
+ case "string":
597
+ case "number":
598
+ case "date":
599
+ case "boolean":
600
+ case "object":
601
+ case "array":
602
+ return schema.kind;
603
+ case "enum": {
604
+ if (Array.isArray(schema.enumValues) && schema.enumValues.length > 0) {
605
+ return schema.enumValues.join("|");
606
+ }
607
+ return "unknown";
608
+ }
609
+ case "literal": {
610
+ const lit = schema.literal;
611
+ if (typeof lit === "string") return "string";
612
+ if (typeof lit === "number") return "number";
613
+ if (typeof lit === "boolean") return "boolean";
614
+ return "unknown";
615
+ }
616
+ default:
617
+ return "unknown";
618
+ }
619
+ }
620
+ function isNonEmptyPath(path3) {
621
+ return typeof path3 === "string" && path3.length > 0;
622
+ }
623
+ function setNode(out, path3, schema, inherited) {
624
+ if (!isNonEmptyPath(path3)) return;
625
+ out[path3] = {
626
+ type: normalizeType(schema),
627
+ nullable: inherited.nullable || Boolean(schema.nullable),
628
+ optional: inherited.optional || Boolean(schema.optional)
629
+ };
630
+ }
631
+ function flattenInto(out, schema, path3, inherited) {
632
+ if (!schema || !isNonEmptyPath(path3)) return;
633
+ const nextInherited = {
634
+ optional: inherited.optional || Boolean(schema.optional),
635
+ nullable: inherited.nullable || Boolean(schema.nullable)
636
+ };
637
+ if (schema.kind === "union" && Array.isArray(schema.union) && schema.union.length > 0) {
638
+ schema.union.forEach((option, index) => {
639
+ const optionPath = `${path3}-${index + 1}`;
640
+ flattenInto(out, option, optionPath, inherited);
641
+ });
642
+ return;
643
+ }
644
+ setNode(out, path3, schema, inherited);
645
+ if (schema.kind === "object" && schema.properties) {
646
+ for (const [key, child] of Object.entries(schema.properties)) {
647
+ const childPath = `${path3}.${key}`;
648
+ flattenInto(out, child, childPath, nextInherited);
649
+ }
650
+ return;
651
+ }
652
+ if (schema.kind === "array" && schema.element) {
653
+ flattenInto(out, schema.element, `${path3}[]`, nextInherited);
654
+ }
655
+ }
656
+ function flattenSerializableSchema(schema, path3) {
657
+ const out = {};
658
+ flattenInto(out, schema, path3, { optional: false, nullable: false });
659
+ return out;
660
+ }
661
+ function isSerializedLeaf(value) {
662
+ if (!value || typeof value !== "object") return false;
663
+ const item = value;
664
+ return typeof item.key === "string" && !!item.cfg && typeof item.cfg === "object";
665
+ }
666
+ function flattenLeafSchemas(leaf) {
667
+ const serialized = isSerializedLeaf(leaf) ? leaf : serializeLeafContract(leaf);
668
+ const out = {};
669
+ const sections = serialized.cfg.schemas;
670
+ Object.assign(out, flattenSerializableSchema(sections.params, "params"));
671
+ Object.assign(out, flattenSerializableSchema(sections.query, "query"));
672
+ Object.assign(out, flattenSerializableSchema(sections.body, "body"));
673
+ Object.assign(out, flattenSerializableSchema(sections.output, "output"));
674
+ return out;
675
+ }
676
+
677
+ // src/export/exportFinalizedLeaves.ts
678
+ import fs from "fs/promises";
679
+ import path from "path";
680
+ function isRegistry(value) {
681
+ return typeof value === "object" && value !== null && "all" in value && "byKey" in value;
682
+ }
683
+ function getLeaves(input) {
684
+ return isRegistry(input) ? input.all : input;
685
+ }
686
+ function buildMeta() {
687
+ return {
688
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
689
+ description: "Finalized RRRoutes leaves export with serialized schemas and flattened schema paths for downstream processing.",
690
+ fieldCatalog: {
691
+ leaf: ["key", "method", "path", "cfg"],
692
+ cfg: [
693
+ "description?",
694
+ "summary?",
695
+ "docsGroup?",
696
+ "tags?",
697
+ "deprecated?",
698
+ "stability?",
699
+ "docsHidden?",
700
+ "docsMeta?",
701
+ "feed?",
702
+ "bodyFiles?",
703
+ "schemas"
704
+ ],
705
+ schemaNode: [
706
+ "kind",
707
+ "optional?",
708
+ "nullable?",
709
+ "description?",
710
+ "properties?",
711
+ "element?",
712
+ "union?",
713
+ "literal?",
714
+ "enumValues?"
715
+ ],
716
+ flatSchemaEntry: ["type", "nullable", "optional"]
717
+ },
718
+ flattening: {
719
+ notation: "dot+[]",
720
+ unionBranchSuffix: "-N",
721
+ sections: ["params", "query", "body", "output"]
722
+ }
723
+ };
724
+ }
725
+ async function writeFinalizedLeavesExport(payload, outFile) {
726
+ const resolved = path.resolve(outFile);
727
+ await fs.mkdir(path.dirname(resolved), { recursive: true });
728
+ await fs.writeFile(resolved, `${JSON.stringify(payload, null, 2)}
729
+ `, "utf8");
730
+ return resolved;
731
+ }
732
+ async function exportFinalizedLeaves(input, options = {}) {
733
+ const leaves = getLeaves(input);
734
+ const serializedLeaves = serializeLeavesContract(leaves, options);
735
+ const schemaFlatByLeaf = Object.fromEntries(
736
+ serializedLeaves.map((leaf) => [leaf.key, flattenLeafSchemas(leaf)])
737
+ );
738
+ const payload = {
739
+ _meta: buildMeta(),
740
+ leaves: serializedLeaves,
741
+ schemaFlatByLeaf
742
+ };
743
+ if (options.outFile) {
744
+ await writeFinalizedLeavesExport(payload, options.outFile);
745
+ }
746
+ return payload;
747
+ }
748
+
749
+ // src/export/exportFinalizedLeaves.cli.ts
750
+ import path2 from "path";
751
+ import { pathToFileURL } from "url";
752
+ function parseFinalizedLeavesCliArgs(argv) {
753
+ const args = /* @__PURE__ */ new Map();
754
+ for (let i = 0; i < argv.length; i += 1) {
755
+ const key = argv[i];
756
+ if (key === "--") continue;
757
+ if (!key.startsWith("--")) continue;
758
+ const value = argv[i + 1];
759
+ if (!value || value.startsWith("--")) {
760
+ throw new Error(`Missing value for ${key}`);
761
+ }
762
+ args.set(key, value);
763
+ i += 1;
764
+ }
765
+ const modulePath = args.get("--module");
766
+ if (!modulePath) {
767
+ throw new Error("Missing required --module argument");
768
+ }
769
+ return {
770
+ modulePath,
771
+ exportName: args.get("--export") ?? "leaves",
772
+ outFile: args.get("--out") ?? "finalized-leaves.export.json"
773
+ };
774
+ }
775
+ async function loadFinalizedLeavesInput({
776
+ modulePath,
777
+ exportName
778
+ }) {
779
+ const resolvedModule = path2.resolve(process.cwd(), modulePath);
780
+ const mod = await import(pathToFileURL(resolvedModule).href);
781
+ const value = mod[exportName] ?? (mod.default && mod.default[exportName]);
782
+ if (!value) {
783
+ throw new Error(`Export "${exportName}" not found in module: ${resolvedModule}`);
784
+ }
785
+ return value;
786
+ }
787
+ async function runExportFinalizedLeavesCli(argv) {
788
+ const args = parseFinalizedLeavesCliArgs(argv);
789
+ const input = await loadFinalizedLeavesInput(args);
790
+ const payload = await exportFinalizedLeaves(input, { outFile: args.outFile });
791
+ return {
792
+ payload,
793
+ outFile: path2.resolve(args.outFile)
794
+ };
795
+ }
338
796
  export {
339
797
  buildCacheKey,
340
798
  buildLowProfileLeaf,
799
+ clearSchemaIntrospectionHandlers,
341
800
  collectNestedFieldSuggestions,
342
801
  compilePath,
802
+ createSchemaIntrospector,
343
803
  defineSocketEvents,
804
+ exportFinalizedLeaves,
344
805
  finalize,
806
+ flattenLeafSchemas,
807
+ flattenSerializableSchema,
345
808
  getZodShape,
809
+ introspectSchema,
346
810
  keyOf,
811
+ loadFinalizedLeavesInput,
347
812
  lowProfileParse,
348
813
  lowProfileSafeParse,
349
814
  mergeArrays,
350
815
  mergeSchemas,
816
+ parseFinalizedLeavesCliArgs,
817
+ registerSchemaIntrospectionHandler,
351
818
  resource,
352
819
  routeSchemaParse,
353
- staticBase
820
+ runExportFinalizedLeavesCli,
821
+ serializableSchemaKinds,
822
+ serializeLeafContract,
823
+ serializeLeavesContract,
824
+ staticBase,
825
+ writeFinalizedLeavesExport
354
826
  };
355
827
  //# sourceMappingURL=index.mjs.map