@emeryld/rrroutes-contract 2.2.10 → 2.2.12

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,10 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { AnyLeaf, Append, HttpMethod, Leaf, Merge, MergeArray, MethodCfg, NodeCfg, Prettify } from './routesV3.core';
3
- declare const paginationField: z.ZodDefault<z.ZodOptional<z.ZodObject<{
4
- cursor: z.ZodOptional<z.ZodString>;
5
- limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
6
- }, z.core.$strip>>>;
7
- type PaginationField = typeof paginationField;
3
+ declare const paginationQueryShape: {
4
+ pagination_cursor: z.ZodOptional<z.ZodString>;
5
+ pagination_limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
6
+ };
7
+ type PaginationShape = typeof paginationQueryShape;
8
8
  type ZodTypeAny = z.ZodTypeAny;
9
9
  type ParamZod<Name extends string, S extends ZodTypeAny> = z.ZodObject<{
10
10
  [K in Name]: S;
@@ -29,11 +29,7 @@ type FeedField<C extends MethodCfg> = C['feed'] extends true ? {
29
29
  } : {
30
30
  feed?: boolean;
31
31
  };
32
- type AddPaginationToQuery<Q extends AnyZodObject | undefined> = Q extends z.ZodObject<infer Shape> ? z.ZodObject<Shape & {
33
- pagination: PaginationField;
34
- }> : z.ZodObject<{
35
- pagination: PaginationField;
36
- }>;
32
+ type AddPaginationToQuery<Q extends AnyZodObject | undefined> = Q extends z.ZodObject<infer Shape> ? z.ZodObject<Shape & PaginationShape> : z.ZodObject<PaginationShape>;
37
33
  type FeedQueryField<C extends MethodCfg> = {
38
34
  querySchema: AddPaginationToQuery<C['querySchema'] extends AnyZodObject ? C['querySchema'] : undefined>;
39
35
  };
@@ -2,21 +2,17 @@ import { z, ZodType } from 'zod';
2
2
  import { resource as baseResource, type Branch as _Branch } from '../core/routesV3.builder';
3
3
  import { type AnyLeaf, type Leaf, type NodeCfg } from '../core/routesV3.core';
4
4
  /** Default cursor pagination used by list (GET collection). */
5
+ declare const crudPaginationShape: {
6
+ pagination_cursor: z.ZodOptional<z.ZodString>;
7
+ pagination_limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
8
+ };
5
9
  export declare const CrudDefaultPagination: z.ZodObject<{
6
- limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
7
- cursor: z.ZodOptional<z.ZodString>;
10
+ pagination_cursor: z.ZodOptional<z.ZodString>;
11
+ pagination_limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
8
12
  }, z.core.$strip>;
9
- declare const CrudPaginationField: z.ZodDefault<z.ZodOptional<z.ZodObject<{
10
- limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
11
- cursor: z.ZodOptional<z.ZodString>;
12
- }, z.core.$strip>>>;
13
- type CrudPaginationField = typeof CrudPaginationField;
13
+ type CrudPaginationShape = typeof crudPaginationShape;
14
14
  type AnyCrudZodObject = z.ZodObject<any>;
15
- type AddCrudPagination<Q extends AnyCrudZodObject | undefined> = Q extends z.ZodObject<infer Shape> ? z.ZodObject<Shape & {
16
- pagination: CrudPaginationField;
17
- }> : z.ZodObject<{
18
- pagination: CrudPaginationField;
19
- }>;
15
+ type AddCrudPagination<Q extends AnyCrudZodObject | undefined> = Q extends z.ZodObject<infer Shape> ? z.ZodObject<Shape & CrudPaginationShape> : z.ZodObject<CrudPaginationShape>;
20
16
  /**
21
17
  * Merge two Zod schemas at runtime; matches the typing pattern used in builder.
22
18
  * @param a Existing params schema inherited from parent branches.
package/dist/index.cjs CHANGED
@@ -46,11 +46,30 @@ module.exports = __toCommonJS(index_exports);
46
46
 
47
47
  // src/core/routesV3.builder.ts
48
48
  var import_zod = require("zod");
49
- var defaultFeedQuerySchema = import_zod.z.object({
50
- cursor: import_zod.z.string().optional(),
51
- limit: import_zod.z.coerce.number().min(1).max(100).default(20)
52
- });
53
- var paginationField = defaultFeedQuerySchema.optional().default(defaultFeedQuerySchema.parse({}));
49
+ var paginationQueryShape = {
50
+ pagination_cursor: import_zod.z.string().optional(),
51
+ pagination_limit: import_zod.z.coerce.number().min(1).max(100).default(20)
52
+ };
53
+ var defaultFeedQuerySchema = import_zod.z.object(paginationQueryShape);
54
+ function getZodShape(schema) {
55
+ const shapeOrGetter = schema.shape ? schema.shape : schema._def?.shape?.();
56
+ if (!shapeOrGetter) return {};
57
+ return typeof shapeOrGetter === "function" ? shapeOrGetter.call(schema) : shapeOrGetter;
58
+ }
59
+ function collectNestedFieldSuggestions(shape, prefix = []) {
60
+ if (!shape) return [];
61
+ const suggestions = [];
62
+ for (const [key, value] of Object.entries(shape)) {
63
+ if (value instanceof import_zod.z.ZodObject) {
64
+ const nestedShape = getZodShape(value);
65
+ const nestedSuggestions = collectNestedFieldSuggestions(nestedShape, [...prefix, key]);
66
+ suggestions.push(...nestedSuggestions.length ? nestedSuggestions : [[...prefix, key].join("_")]);
67
+ } else if (prefix.length > 0) {
68
+ suggestions.push([...prefix, key].join("_"));
69
+ }
70
+ }
71
+ return suggestions;
72
+ }
54
73
  var defaultFeedOutputSchema = import_zod.z.object({
55
74
  items: import_zod.z.array(import_zod.z.unknown()),
56
75
  nextCursor: import_zod.z.string().optional()
@@ -58,14 +77,17 @@ var defaultFeedOutputSchema = import_zod.z.object({
58
77
  function augmentFeedQuerySchema(schema) {
59
78
  if (schema && !(schema instanceof import_zod.z.ZodObject)) {
60
79
  console.warn("Feed queries must be a ZodObject; default pagination applied.");
61
- return import_zod.z.object({
62
- pagination: paginationField
63
- });
80
+ return defaultFeedQuerySchema;
64
81
  }
65
82
  const base = schema ?? import_zod.z.object({});
66
- return base.extend({
67
- pagination: paginationField
68
- });
83
+ const shape = getZodShape(base);
84
+ const nestedSuggestions = collectNestedFieldSuggestions(shape);
85
+ if (nestedSuggestions.length) {
86
+ console.warn(
87
+ `Feed query schemas should avoid nested objects; consider flattening fields like: ${nestedSuggestions.join(", ")}`
88
+ );
89
+ }
90
+ return base.extend(paginationQueryShape);
69
91
  }
70
92
  function augmentFeedOutputSchema(schema) {
71
93
  if (!schema) return defaultFeedOutputSchema;
@@ -227,11 +249,11 @@ function finalize(leaves) {
227
249
 
228
250
  // src/crud/routesV3.crud.ts
229
251
  var import_zod2 = require("zod");
230
- var CrudDefaultPagination = import_zod2.z.object({
231
- limit: import_zod2.z.coerce.number().min(1).max(100).default(20),
232
- cursor: import_zod2.z.string().optional()
233
- });
234
- var CrudPaginationField = CrudDefaultPagination.optional().default(CrudDefaultPagination.parse({}));
252
+ var crudPaginationShape = {
253
+ pagination_cursor: import_zod2.z.string().optional(),
254
+ pagination_limit: import_zod2.z.coerce.number().min(1).max(100).default(20)
255
+ };
256
+ var CrudDefaultPagination = import_zod2.z.object(crudPaginationShape);
235
257
  function mergeSchemas2(a, b) {
236
258
  if (a && b) return import_zod2.z.intersection(a, b);
237
259
  return a ?? b;
@@ -644,6 +666,28 @@ body {
644
666
  .schema-toggle:hover { color: var(--text-main); }
645
667
  tr[data-hidden="true"] { display: none; }
646
668
 
669
+ /* Schema name cell indentation */
670
+ .schema-name-cell {
671
+ display: flex;
672
+ align-items: center;
673
+ }
674
+ .schema-name-text {
675
+ opacity: 0.8;
676
+ }
677
+ .schema-toggle-placeholder {
678
+ width: 20px;
679
+ display: inline-block;
680
+ }
681
+ .schema-depth-0 { padding-left: 0; }
682
+ .schema-depth-1 { padding-left: 16px; }
683
+ .schema-depth-2 { padding-left: 32px; }
684
+ .schema-depth-3 { padding-left: 48px; }
685
+ .schema-depth-4 { padding-left: 64px; }
686
+ .schema-depth-5 { padding-left: 80px; }
687
+ .schema-depth-6 { padding-left: 96px; }
688
+ .schema-depth-7 { padding-left: 112px; }
689
+ .schema-depth-8 { padding-left: 128px; }
690
+
647
691
  /* --- Playground Panel --- */
648
692
  .playground-overlay {
649
693
  position: fixed; top: 0; right: 0; bottom: 0; width: 600px; max-width: 90vw;
@@ -659,6 +703,14 @@ tr[data-hidden="true"] { display: none; }
659
703
  padding: 40px;
660
704
  color: #64748b;
661
705
  }
706
+
707
+ /* Empty-state for no results */
708
+ .no-results {
709
+ text-align: center;
710
+ padding: 40px;
711
+ color: var(--text-muted);
712
+ }
713
+
662
714
  .section-header-row {
663
715
  display:flex;
664
716
  justify-content:space-between;
@@ -708,10 +760,25 @@ tr[data-hidden="true"] { display: none; }
708
760
  .pg-input:focus, .pg-textarea:focus { outline: none; border-color: var(--accent-primary); }
709
761
  .pg-textarea { min-height: 150px; resize: vertical; }
710
762
 
763
+ /* Disabled label input in playground */
764
+ .pg-input-disabled-label {
765
+ opacity: 0.7;
766
+ }
767
+
711
768
  /* Param Grid */
712
769
  .param-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
713
770
  .param-row { display: contents; }
714
771
 
772
+ /* Add query button */
773
+ .btn-add-query {
774
+ font-size: 11px;
775
+ background: none;
776
+ border: 1px dashed var(--border-subtle);
777
+ color: var(--text-muted);
778
+ padding: 4px;
779
+ cursor: pointer;
780
+ }
781
+
715
782
  .pg-footer {
716
783
  padding: 16px; border-top: 1px solid var(--border-subtle); background: var(--bg-glass);
717
784
  display: flex; justify-content: flex-end; gap: 12px;
@@ -734,6 +801,16 @@ tr[data-hidden="true"] { display: none; }
734
801
  .status-err { color: var(--accent-error); }
735
802
  .response-body { padding: 12px; overflow-x: auto; font-family: var(--font-mono); font-size: 12px; }
736
803
 
804
+ /* Response visibility */
805
+ .pg-response-hidden {
806
+ display: none;
807
+ }
808
+
809
+ /* Preformatted response text */
810
+ .response-pre {
811
+ margin: 0;
812
+ }
813
+
737
814
  /* JSON Tree */
738
815
  .json-tree { font-family: var(--font-mono); line-height: 1.6; }
739
816
  .jt-obj, .jt-arr { margin-left: 14px; }
@@ -749,6 +826,13 @@ tr[data-hidden="true"] { display: none; }
749
826
  .jt-toggle:hover { color: var(--text-main); }
750
827
  .jt-collapsed .jt-content { display: none; }
751
828
  .jt-collapsed::after { content: " ... "; color: var(--text-muted); font-size: 10px; }
829
+
830
+ /* JSON tree line indentation */
831
+ .json-tree-line {
832
+ padding-left: 16px;
833
+ }
834
+
835
+ .hidden-template { display: none; }
752
836
  `;
753
837
  var DOCS_JS = `
754
838
  (function() {
@@ -933,7 +1017,7 @@ var DOCS_JS = `
933
1017
  });
934
1018
 
935
1019
  if (filtered.length === 0) {
936
- elRouteList.innerHTML = '<div style="text-align:center; padding:40px; color:var(--text-muted)">No endpoints match filters.</div>';
1020
+ elRouteList.innerHTML = '<div class="no-results">No endpoints match filters.</div>';
937
1021
  elOverview.innerHTML = '';
938
1022
  return;
939
1023
  }
@@ -1021,21 +1105,25 @@ var DOCS_JS = `
1021
1105
  function renderNodeRow(name, node, depth, parentPath) {
1022
1106
  const hasChildren = isComplexNode(node) && ((node.properties && Object.keys(node.properties).length > 0) || (node.element && isComplexNode(node.element)) || (node.union));
1023
1107
  const currentPath = (parentPath ? parentPath + '.' : '') + name;
1024
- const indent = depth * 16;
1025
1108
 
1026
1109
  // NEW: Collapse by default. Hide deeply nested rows (depth > 0) initially.
1027
1110
  const isExpanded = false;
1028
1111
  const isHidden = depth > 0;
1029
1112
 
1030
- const styleAttr = isHidden ? 'style="display:none"' : '';
1031
- const rowAttrs = 'data-depth="'+depth+'" data-path="'+escapeAttr(currentPath)+'" '+styleAttr + (hasChildren ? ' data-has-children="true" data-expanded="'+isExpanded+'"' : '');
1113
+ const classes = [];
1114
+ if (isHidden) classes.push('hidden-template');
1115
+
1116
+ const rowAttrs = 'data-depth="'+depth+'" data-path="'+escapeAttr(currentPath)+'"' +
1117
+ (classes.length ? ' class="'+classes.join(' ')+'"' : '') +
1118
+ (hasChildren ? ' data-has-children="true" data-expanded="'+isExpanded+'"' : '');
1032
1119
 
1033
1120
  const toggleHtml = hasChildren
1034
1121
  ? '<button class="schema-toggle">' + (isExpanded ? '\u25BC' : '\u25B6') + '</button>'
1035
- : '<span style="width:20px; display:inline-block;"></span>';
1122
+ : '<span class="schema-toggle-placeholder"></span>';
1036
1123
 
1037
- const nameCell = '<div style="padding-left:'+indent+'px; display:flex; align-items:center;">' +
1038
- toggleHtml + '<span style="opacity:0.8">'+escapeHtml(name)+'</span></div>';
1124
+ const depthClass = 'schema-depth-' + depth;
1125
+ const nameCell = '<div class="schema-name-cell '+depthClass+'">' +
1126
+ toggleHtml + '<span class="schema-name-text">'+escapeHtml(name)+'</span></div>';
1039
1127
 
1040
1128
  let html = '<tr '+rowAttrs+'>' +
1041
1129
  '<td class="col-name">' + nameCell + '</td>' +
@@ -1091,7 +1179,7 @@ var DOCS_JS = `
1091
1179
  checkVisibility(sibling);
1092
1180
  } else {
1093
1181
  // COLLAPSING: Hide all descendants regardless of their state
1094
- sibling.style.display = 'none';
1182
+ sibling.classList.add('hidden-template');
1095
1183
  }
1096
1184
  sibling = sibling.nextElementSibling;
1097
1185
  }
@@ -1105,18 +1193,19 @@ var DOCS_JS = `
1105
1193
  const pDepth = parseInt(prev.getAttribute('data-depth'), 10);
1106
1194
  if (pDepth < myDepth) {
1107
1195
  // Found parent
1108
- // If parent is expanded AND visible, then I should be visible.
1109
- if (prev.getAttribute('data-expanded') === 'true' && prev.style.display !== 'none') {
1110
- row.style.display = '';
1196
+ const parentVisible = !prev.classList.contains('hidden-template');
1197
+ // If parent is expanded AND visible, then this should be visible.
1198
+ if (prev.getAttribute('data-expanded') === 'true' && parentVisible) {
1199
+ row.classList.remove('hidden-template');
1111
1200
  } else {
1112
- row.style.display = 'none';
1201
+ row.classList.add('hidden-template');
1113
1202
  }
1114
1203
  return;
1115
1204
  }
1116
1205
  prev = prev.previousElementSibling;
1117
1206
  }
1118
1207
  // No parent found (depth 0), always visible
1119
- row.style.display = '';
1208
+ row.classList.remove('hidden-template');
1120
1209
  }
1121
1210
 
1122
1211
  function toggleAllSchema(table, expand) {
@@ -1130,7 +1219,7 @@ var DOCS_JS = `
1130
1219
  // Update visibility for all rows
1131
1220
  const allRows = table.querySelectorAll('tr[data-depth]');
1132
1221
  allRows.forEach(row => {
1133
- if(parseInt(row.getAttribute('data-depth')) === 0) return;
1222
+ if(parseInt(row.getAttribute('data-depth'), 10) === 0) return;
1134
1223
  checkVisibility(row);
1135
1224
  });
1136
1225
  }
@@ -1174,7 +1263,7 @@ var DOCS_JS = `
1174
1263
  matches.forEach(m => {
1175
1264
  const key = m[1];
1176
1265
  html += '<div class="param-row">' +
1177
- '<input class="pg-input" placeholder="'+key+'" disabled value="'+key+'" style="opacity:0.7">' +
1266
+ '<input class="pg-input pg-input-disabled-label" placeholder="'+key+'" disabled value="'+key+'">' +
1178
1267
  '<input class="pg-input pg-path-input" data-key="'+key+'" placeholder="value">' +
1179
1268
  '</div>';
1180
1269
  });
@@ -1185,7 +1274,7 @@ var DOCS_JS = `
1185
1274
  // We check leaf.cfg.querySchema or just provide a generic adder
1186
1275
  html += '<div class="pg-section"><div class="pg-label">Query Parameters</div>' +
1187
1276
  '<div class="param-grid" id="pgQueryGrid"></div>' +
1188
- '<button type="button" id="pgAddQuery" style="font-size:11px;background:none;border:1px dashed var(--border-subtle);color:var(--text-muted);padding:4px;cursor:pointer;">+ Add Parameter</button>' +
1277
+ '<button type="button" id="pgAddQuery" class="btn-add-query">+ Add Parameter</button>' +
1189
1278
  '</div>';
1190
1279
 
1191
1280
  // 4. Body
@@ -1196,7 +1285,7 @@ var DOCS_JS = `
1196
1285
  }
1197
1286
 
1198
1287
  // 5. Response Container
1199
- html += '<div class="pg-section" id="pgResponseContainer" style="display:none;">' +
1288
+ html += '<div class="pg-section pg-response-hidden" id="pgResponseContainer">' +
1200
1289
  '<div class="pg-label">Response</div>' +
1201
1290
  '<div class="response-box">' +
1202
1291
  '<div class="response-meta" id="pgResponseMeta"></div>' +
@@ -1236,7 +1325,7 @@ var DOCS_JS = `
1236
1325
 
1237
1326
  btn.disabled = true;
1238
1327
  btn.textContent = 'Sending...';
1239
- resContainer.style.display = 'none';
1328
+ resContainer.classList.add('pg-response-hidden');
1240
1329
 
1241
1330
  try {
1242
1331
  // Build URL
@@ -1296,7 +1385,7 @@ var DOCS_JS = `
1296
1385
  }
1297
1386
 
1298
1387
  // Render Response
1299
- resContainer.style.display = 'block';
1388
+ resContainer.classList.remove('pg-response-hidden');
1300
1389
  const statusClass = (status >= 200 && status < 300) ? 'status-ok' : 'status-err';
1301
1390
  metaEl.innerHTML = '<div class="meta-item '+statusClass+'">\u2B24 '+status+' '+statusText+'</div><div class="meta-item">\u23F1 '+time+'ms</div>';
1302
1391
 
@@ -1305,13 +1394,13 @@ var DOCS_JS = `
1305
1394
  bodyEl.appendChild(renderJsonTree(resData));
1306
1395
  } else {
1307
1396
  const pre = document.createElement('pre');
1308
- pre.style.margin = 0;
1397
+ pre.className = 'response-pre';
1309
1398
  pre.textContent = resData;
1310
1399
  bodyEl.appendChild(pre);
1311
1400
  }
1312
1401
 
1313
1402
  } catch (err) {
1314
- resContainer.style.display = 'block';
1403
+ resContainer.classList.remove('pg-response-hidden');
1315
1404
  metaEl.innerHTML = '<div class="meta-item status-err">Network Error</div>';
1316
1405
  bodyEl.textContent = err.message;
1317
1406
  } finally {
@@ -1352,7 +1441,7 @@ var DOCS_JS = `
1352
1441
 
1353
1442
  keys.forEach((k, i) => {
1354
1443
  const line = document.createElement('div');
1355
- line.style.paddingLeft = '16px';
1444
+ line.className = 'json-tree-line';
1356
1445
 
1357
1446
  if (!isArr) {
1358
1447
  const keySpan = createSpan('"'+k+'": ', 'jt-key');
@@ -1433,7 +1522,7 @@ var DOCS_JS = `
1433
1522
  `;
1434
1523
  function renderLeafDocsHTML(leaves, options = {}) {
1435
1524
  const leavesJson = JSON.stringify(leaves.map(serializeLeaf));
1436
- const nonceAttr = options.cspNonce ? ` nonce="${options.cspNonce}"` : "";
1525
+ const nonceAttr = options.cspNonce ? ` nonce="\${options.cspNonce}"` : "";
1437
1526
  return `
1438
1527
  <!DOCTYPE html>
1439
1528
  <html lang="en">
@@ -1478,8 +1567,8 @@ function renderLeafDocsHTML(leaves, options = {}) {
1478
1567
  </main>
1479
1568
  </div>
1480
1569
 
1481
- <script id="leaf-data" type="application/json">${leavesJson}</script>
1482
- <script${nonceAttr}>${DOCS_JS}</script>
1570
+ <script id="leaf-data" type="application/json">\${leavesJson}</script>
1571
+ <script ${nonceAttr}> ${DOCS_JS}</script>
1483
1572
  ${options.inlineClientScript ? `<script${nonceAttr}>${options.inlineClientScript}</script>` : ""}
1484
1573
  </body>
1485
1574
  </html>