route-sprout 1.0.0 → 3.0.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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![Render Slot Image](./image-small.png)
1
+ ![Route Sprout Image](./image.png)
2
2
 
3
3
  # route-sprout 🌱 (typed API route builder DSL)
4
4
 
@@ -10,14 +10,6 @@
10
10
 
11
11
  A tiny, cute DSL that grows **type-safe, composable URL builders** from a declarative route tree.
12
12
 
13
- - ✅ Strong TypeScript inference from your route definition
14
- - ✅ Nested resources with `slot('id')` parameters
15
- - ✅ Optional path gates with `wrap('admin', predicate, …)`
16
- - ✅ Ad‑hoc conditional segments anywhere with `.when(cond, segments) and join(segments)`
17
- - ✅ Search params supported (`string` or `URLSearchParams`)
18
-
19
- > Think of it as a little route bonsai: you shape the tree once, then pluck URLs from any branch.
20
-
21
13
  ---
22
14
 
23
15
  ## Install
@@ -39,6 +31,7 @@ const Api = root([
39
31
  path("invoices", [
40
32
  keep(),
41
33
  slot("id", [
34
+ keep(),
42
35
  path("price"),
43
36
  path("customers")
44
37
  ]),
@@ -48,12 +41,22 @@ const Api = root([
48
41
 
49
42
  Api.invoices(); // "/invoices"
50
43
  Api.invoices("page=1"); // "/invoices?page=1"
51
- Api.invoices.id("abc")("a=1"); // "/invoices/abc?a=1"
52
- Api.invoices.id("abc").customers(); // "/invoices/abc/customers"
44
+ Api.invoices.$id("abc")("a=1"); // "/invoices/abc?a=1"
45
+ Api.invoices.$id("abc").customers(); // "/invoices/abc/customers"
53
46
  ```
54
47
 
55
48
  ---
56
49
 
50
+ - ✅ Strong TypeScript inference from your route definition
51
+ - ✅ Nested resources with `slot('id')` parameters
52
+ - ✅ Optional path gates with `wrap('admin', predicate, …)`
53
+ - ✅ Ad‑hoc conditional segments anywhere with `.$when(cond, segments) and join(segments)`
54
+ - ✅ Search params supported (`string` or `URLSearchParams`)
55
+
56
+ > Think of it as a little route bonsai: you shape the tree once, then pluck URLs from any branch.
57
+
58
+ ---
59
+
57
60
  ## Why this exists
58
61
 
59
62
  When you have lots of endpoints, you usually end up with:
@@ -66,13 +69,17 @@ When you have lots of endpoints, you usually end up with:
66
69
  This DSL gives you:
67
70
 
68
71
  - a single, declarative source of truth
69
- - fluent, discoverable usage (`Api.invoices.id("x").customers()`)
72
+ - fluent, discoverable usage (`Api.invoices.$id("x").customers()`)
70
73
  - TypeScript autocomplete and type checking from **usage**, not comments
71
74
 
72
75
  ---
73
76
 
74
77
  ## Concepts
75
78
 
79
+ ### `root(children)`
80
+
81
+ An entry point for your root tree that naturally denotes '/' path.
82
+
76
83
  ### `path(name, children?)`
77
84
 
78
85
  A **static** path segment.
@@ -104,7 +111,7 @@ The `name` is only the **property key**. It is **not** inserted into the path.
104
111
 
105
112
  ```ts
106
113
  path("invoices", [slot("id")]);
107
- // Api.invoices.id("abc")() -> "/invoices/abc"
114
+ // Api.invoices.$id("abc")() -> "/invoices/abc"
108
115
  ```
109
116
 
110
117
  With children:
@@ -117,7 +124,7 @@ path("invoices", [
117
124
  ]),
118
125
  ]);
119
126
 
120
- // Api.invoices.id("abc").customers() -> "/invoices/abc/customers"
127
+ // Api.invoices.$id("abc").customers() -> "/invoices/abc/customers"
121
128
  ```
122
129
 
123
130
  ### `wrap(name, predicate, children?)`
@@ -136,27 +143,45 @@ path("core", [
136
143
  ]),
137
144
  ]);
138
145
 
139
- Api.core.admin({ isAdmin: true }).invoices(); // "/core/admin/invoices"
140
- Api.core.admin({ isAdmin: false }).invoices(); // "/core/invoices"
146
+ Api.core.$admin({ isAdmin: true }).invoices(); // "/core/admin/invoices"
147
+ Api.core.$admin({ isAdmin: false }).invoices(); // "/core/invoices"
141
148
  ```
142
149
 
143
150
  > `wrap` is ideal for *well-known*, reusable gates: `admin`, `v2`, `tenant`, etc.
144
151
 
145
- ### `.when(cond, segment | segment[])`
152
+ ### `pick(name, segments, children?)`
153
+
154
+ An enumerated segment group *defined in the tree*.
155
+
156
+ ```ts
157
+ type User = { isAdmin: boolean } | null;
158
+
159
+ path("core", [
160
+ pick("role", { admin: "admin", user: ["user", "role"] }, [
161
+ path("invoices", [keep()]),
162
+ ]),
163
+ ]);
164
+
165
+ Api.core.$role("admin").invoices(); // "/core/admin/invoices"
166
+ Api.core.$role("user").invoices(); // "/core/user/role/invoices"
167
+ ```
168
+
169
+ ### `.$when(cond, segment | segment[])`
146
170
 
147
171
  Ad‑hoc conditional segment insertion at **runtime**, anywhere in the chain.
148
172
 
149
173
  ```ts
150
- Api.core.when(isAdmin, "admin").invoices();
151
- Api.core.when(true, ["tenant", tenantId]).invoices();
152
- Api.invoices.id("abc").when(flags.preview, "preview").activities();
174
+ Api.core.$when(isAdmin, "admin").invoices();
175
+ Api.core.$when(true, ["tenant", tenantId]).invoices();
176
+ Api.invoices.$id("abc").$when(flags.preview, "preview").activities();
153
177
  ```
154
178
 
155
179
  - `cond = false` → no-op
156
180
  - `segment` can be a single segment or an array of segments
157
181
  - empty segments are ignored (your `url()` filters them out)
158
182
 
159
- > `.when()` is ideal when you don’t want to bake a wrapper into the route tree.
183
+ > `.$when()` is ideal when you don’t want to bake a wrapper into the route tree.
184
+ > `.$join()` can be used in place of $when with condition being always true.
160
185
 
161
186
  ---
162
187
 
@@ -208,18 +233,18 @@ export const Api = root([
208
233
 
209
234
  // usage
210
235
  Api.invoices(); // "/invoices"
211
- Api.invoices.id("123").customers(); // "/invoices/123/customers"
236
+ Api.invoices.$id("123").customers(); // "/invoices/123/customers"
212
237
 
213
238
  // runtime insert
214
- Api.core.when(true, "v2").invoices(); // "/core/v2/invoices"
215
- Api.core.admin({ isAdmin: true }).when(true, "v2").invoices(); // "/core/admin/v2/invoices"
239
+ Api.core.$when(true, "v2").invoices(); // "/core/v2/invoices"
240
+ Api.core.$admin({ isAdmin: true }).$when(true, "v2").invoices(); // "/core/admin/v2/invoices"
216
241
  ```
217
242
 
218
243
  ### Autocomplete-friendly patterns
219
244
  Because everything is computed from the definition tree, your editor can autocomplete:
220
245
 
221
246
  - paths (`Api.invoices`, `Api.orders.export`)
222
- - slots (`Api.invoices.id(…)`)
247
+ - slots (`Api.invoices.$id(…)`)
223
248
  - nested children (`…id("x").customers()`)
224
249
 
225
250
  ---
@@ -229,15 +254,16 @@ Because everything is computed from the definition tree, your editor can autocom
229
254
  ### Exports
230
255
 
231
256
  - `root(defs)`
232
- - `path(name, rest?)`
233
- - `slot(name, rest?)`
234
- - `wrap(name, when, rest?)`
257
+ - `path(name, defs?)`
258
+ - `slot(name, defs?)`
259
+ - `wrap(name, when, defs?)`
260
+ - `pick(name, mode, defs?)`
235
261
  - `keep()`
236
262
 
237
- ### Types
263
+ ### Path level builders
238
264
 
239
- - `Segment = string | number`
240
- - `SParams = string | URLSearchParams`
265
+ - `$when(predicate, segments)`
266
+ - `$join(segments)`
241
267
 
242
268
  ---
243
269
 
@@ -264,7 +290,7 @@ const Api = root([
264
290
  path("invoices", [keep(), slot("id")]),
265
291
  ]);
266
292
 
267
- Api.invoices.id("123")(); // "/invoices/123"
293
+ Api.invoices.$id("123")(); // "/invoices/123"
268
294
  ```
269
295
 
270
296
  #### `route-sprout/dialect-step`
@@ -294,7 +320,7 @@ const Api = grow([
294
320
  ]),
295
321
  ]);
296
322
 
297
- Api.core.admin({ isAdmin: true }).jobs(); // "/core/admin/jobs"
323
+ Api.core.$admin({ isAdmin: true }).jobs(); // "/core/admin/jobs"
298
324
  ```
299
325
 
300
326
  #### `route-sprout/dialect-node`
@@ -307,7 +333,7 @@ const Api = link([
307
333
  node("tasks", [base(), bind("id", [node("logs")])]),
308
334
  ]);
309
335
 
310
- Api.tasks.id("x").logs(); // "/tasks/x/logs"
336
+ Api.tasks.$id("x").logs(); // "/tasks/x/logs"
311
337
  ```
312
338
 
313
339
  ### Mix-and-match?
@@ -319,10 +345,10 @@ Dialects are meant to be **all-in** per codebase/file. Technically you can mix i
319
345
  - `slot("id")` uses `"id"` **only as a property name**, not a URL segment.
320
346
  - ✅ `/invoices/123`
321
347
  - ❌ `/invoices/id/123`
322
- - `.when()` rebuilds a subtree and returns a new object/function.
348
+ - `.$when()` rebuilds a subtree and returns a new object/function.
323
349
  - It does **not** mutate the original branch.
324
350
  - Empty segments are ignored in the final URL (because `url()` does `filter()`).
325
- - If you want stricter behavior (throw on empty segment), enforce it in your own `.when` wrapper.
351
+ - If you want stricter behavior (throw on empty segment), enforce it in your own `.$when` wrapper.
326
352
 
327
353
  ---
328
354
 
@@ -339,7 +365,7 @@ import { root, path, slot, keep } from "route-sprout";
339
365
  test("builds routes", () => {
340
366
  const Api = root([path("invoices", [keep(), slot("id")])] as const);
341
367
  expect(Api.invoices()).toBe("/invoices");
342
- expect(Api.invoices.id("x")()).toBe("/invoices/x");
368
+ expect(Api.invoices.$id("x")()).toBe("/invoices/x");
343
369
  });
344
370
  ```
345
371
 
package/dist/api.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var S=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var g=(n,e)=>{for(var o in e)S(n,o,{get:e[o],enumerable:!0})},k=(n,e,o,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of h(e))!y.call(n,a)&&a!==o&&S(n,a,{get:()=>e[a],enumerable:!(s=u(e,a))||s.enumerable});return n};var D=n=>k(S({},"__esModule",{value:!0}),n);var A={};g(A,{keep:()=>P,path:()=>d,root:()=>x,slot:()=>R,wrap:()=>w});module.exports=D(A);var P=()=>({kind:"keep"}),d=(n,e)=>({kind:"path",name:n,rest:e??[]}),R=(n,e)=>({kind:"slot",name:n,rest:e??[]}),w=(n,e,o)=>({kind:"wrap",name:n,when:e,rest:o??[]}),l=(n,e)=>`${n.filter(Boolean).join("/")}${e?`?${e}`:""}`.replace("//","/");function x(n){return m([],d("/",n))}function m(n,e){let o=t=>t.rest.some(r=>r.kind==="keep"),s=e.kind==="slot"||e.kind==="wrap"?n:e.name?[...n,e.name]:n,a=o(e)?t=>l(s,t):{};for(let t of e.rest)t.kind==="slot"?t.rest.length===0?a[t.name]=r=>i=>l([...s,r],i):a[t.name]=r=>{let i=m([...s,r],t);return Object.assign(o(t)?c=>l([...s,r],c):{},i)}:t.kind==="path"?t.rest.length===0?a[t.name]=r=>l([...s,t.name],r):a[t.name]=m(s,t):t.kind==="wrap"&&(a[t.name]=r=>{let c=t.when(r)?[...s,t.name]:s,p=m(c,t);return Object.assign(o(t)?f=>l(c,f):{},p)});return N(a,s,e.rest)}function N(n,e,o){return n.when=(s,a)=>m(s?[...e,...Array.isArray(a)?a:[a]]:e,d("",o)),n.join=s=>m([...e,...Array.isArray(s)?s:[s]],d("",o)),n}0&&(module.exports={keep,path,root,slot,wrap});
1
+ "use strict";var g=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var w=(t,e)=>{for(var s in e)g(t,s,{get:e[s],enumerable:!0})},y=(t,e,s,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of N(e))!S.call(t,o)&&o!==s&&g(t,o,{get:()=>e[o],enumerable:!(a=$(e,o))||a.enumerable});return t};var x=t=>y(g({},"__esModule",{value:!0}),t);var _={};w(_,{keep:()=>C,path:()=>p,pick:()=>b,root:()=>A,slot:()=>L,wrap:()=>P});module.exports=x(_);var u=t=>t.replace(/^-+/,"").replace(/-+$/,"").replace(/^_+/,"").replace(/_+$/,"").replace(/-([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()).replace(/_([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()),D=/^[A-Za-z_-][A-Za-z0-9_-]*$/;function m(t,e){if(t==="path"&&e==="")return e.trim();if(!e)throw new Error(`${t} name cannot be empty`);if(!D.test(e))throw new Error(`${t} name "${e}" must be a valid identifier (letters/digits/_/$, not starting with a digit).`);return e.trim()}var C=()=>({kind:"keep"}),p=(t,e)=>({kind:"path",name:m("path",t),uuid:u(t),list:e??[]}),L=(t,e)=>({kind:"slot",name:m("slot",t),uuid:`$${u(t)}`,list:e??[]}),P=(t,e,s)=>({kind:"wrap",name:m("wrap",t),uuid:`$${u(t)}`,when:e,list:s??[]}),b=(t,e,s)=>({kind:"pick",name:m("pick",t),uuid:`$${u(t)}`,mode:e,list:s??[]}),A=t=>l([],p("",t)),T=(t,e)=>`/${t.filter(Boolean).join("/").replace(/\/{2,}/g,"/")}${e?`?${e}`:""}`;function l(t,e){let s=n=>n.list.some(r=>r.kind==="keep"),a=e.kind==="path"&&e.name?[...t,e.name]:t,o=i(s(e),a);for(let n of e.list){if(n.kind!=="keep"&&n.uuid in o)throw new Error(`Duplicate uuid "${String(n.uuid)}" under "${a.join("/")||"/"}"`);if(n.kind==="slot")o[n.uuid]=function(f){let d=[...a,f];return n.list.length===0?c(i(!0,d),d,[]):Object.assign(i(s(n),d),l(d,n))};else if(n.kind==="path")if(n.list.length===0){let r=[...a,n.name];o[n.uuid]=c(i(!0,r),r,[])}else o[n.uuid]=l(a,n);else n.kind==="wrap"?o[n.uuid]=function(f){let h=n.when(f)?[...a,n.name]:a,k=l(h,n);return Object.assign(i(s(n),h),k)}:n.kind==="pick"&&(o[n.uuid]=r=>{if(n.mode[r])return l([...a,...n.mode[r]],p("",n.list));throw new Error(`pick("${n.name}") got unknown value: ${String(r)}`)})}return c(o,a,e.list)}function c(t,e,s){let a=(o,n)=>{let r=o?[...e,...Array.isArray(n)?n:[n]]:e;return s.length===0&&typeof t=="function"?c(i(!0,r),r,s):l(r,p("",s))};return t.$when=a,t.$join=function(n){return a(!0,n)},t}var i=(t,e)=>t?s=>T(e,s):Object.create(null);0&&(module.exports={keep,path,pick,root,slot,wrap});
2
2
  //# sourceMappingURL=api.cjs.map
package/dist/api.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api.ts"],"sourcesContent":["import { Keep, Path, Wrap, Slot, PathDef, SlotDef, Segment, SParams, RoutesFromDefs } from './dsl'\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst Rest extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\trest?: Rest\n): Path<Name, Rest> => ({ kind: 'path', name, rest: (rest ?? []) as Rest })\n\nexport const slot = <\n\tconst Name extends string,\n\tconst Rest extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\trest?: Rest\n): Slot<Name, Rest> => ({ kind: 'slot', name, rest: (rest ?? []) as Rest })\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst Rest extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\trest?: Rest\n): Wrap<Name, Rest, Args> => ({ kind: 'wrap', name, when, rest: (rest ?? []) as Rest })\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`${path.filter(Boolean).join('/')}${search ? `?${search}` : ''}`.replace('//', '/')\n\n// ---------- Typed root signature ----------\nexport function root<const Defs extends readonly PathDef[]>(\n\tdefs: Defs\n): RoutesFromDefs<Defs> {\n\treturn buildPath([], path('/', defs)) as unknown as RoutesFromDefs<Defs>\n}\n\nfunction buildPath(prefix: Segment[], def: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.rest.some((c) => c.kind === 'keep')\n\tconst allPath =\n\t\tdef.kind === 'slot' || def.kind === 'wrap'\n\t\t\t? prefix\n\t\t\t: def.name\n\t\t\t\t? [...prefix, def.name]\n\t\t\t\t: prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = hasKeep(def) ? (search?: SParams) => url(allPath, search) : {}\n\n\tfor (const child of def.rest) {\n\t\tif (child.kind === 'slot') {\n\t\t\tif (child.rest.length === 0) {\n\t\t\t\ttarget[child.name] = (param: Segment) => (search?: SParams) =>\n\t\t\t\t\turl([...allPath, param], search)\n\t\t\t} else {\n\t\t\t\ttarget[child.name] = (param: Segment) => {\n\t\t\t\t\t// Build subtree for nested parts under :id\n\t\t\t\t\t// Synthetic path with empty name so we don't add extra segment.\n\t\t\t\t\tconst subTree = buildPath([...allPath, param], child)\n\n\t\t\t\t\t// Attach children (info, activities, etc.) to that function\n\t\t\t\t\treturn Object.assign(\n\t\t\t\t\t\thasKeep(child)\n\t\t\t\t\t\t\t? (search?: SParams) => url([...allPath, param], search)\n\t\t\t\t\t\t\t: {},\n\t\t\t\t\t\tsubTree\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.rest.length === 0) {\n\t\t\t\ttarget[child.name] = (search?: SParams) => url([...allPath, child.name], search)\n\t\t\t} else {\n\t\t\t\ttarget[child.name] = buildPath(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.name] = (arg: unknown) => {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildPath(wrapped, child as any)\n\n\t\t\t\treturn Object.assign(\n\t\t\t\t\t// if wrap has keep(), it becomes callable at that point\n\t\t\t\t\thasKeep(child as any) ? (search?: SParams) => url(wrapped, search) : {},\n\t\t\t\t\tsubTree\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, def.rest)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], rest: readonly PathDef[]) {\n\ttarget.when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildPath(\n\t\t\tcond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath,\n\t\t\tpath('', rest)\n\t\t)\n\t}\n\ttarget.join = (seg: Segment | readonly Segment[]) => {\n\t\treturn buildPath([...basePath, ...(Array.isArray(seg) ? seg : [seg])], path('', rest))\n\t}\n\n\treturn target\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,UAAAE,EAAA,SAAAC,EAAA,SAAAC,EAAA,SAAAC,EAAA,SAAAC,IAAA,eAAAC,EAAAP,GAGO,IAAME,EAAO,KAAa,CAAE,KAAM,MAAO,GAEnCC,EAAO,CAInBK,EACAC,KACuB,CAAE,KAAM,OAAQ,KAAAD,EAAM,KAAOC,GAAQ,CAAC,CAAW,GAE5DJ,EAAO,CAInBG,EACAC,KACuB,CAAE,KAAM,OAAQ,KAAAD,EAAM,KAAOC,GAAQ,CAAC,CAAW,GAE5DH,EAAO,CAKnBE,EACAE,EACAD,KAC6B,CAAE,KAAM,OAAQ,KAAAD,EAAM,KAAAE,EAAM,KAAOD,GAAQ,CAAC,CAAW,GAG/EE,EAAM,CAACR,EAAiBS,IAC7B,GAAGT,EAAK,OAAO,OAAO,EAAE,KAAK,GAAG,CAAC,GAAGS,EAAS,IAAIA,CAAM,GAAK,EAAE,GAAG,QAAQ,KAAM,GAAG,EAG5E,SAASR,EACfS,EACuB,CACvB,OAAOC,EAAU,CAAC,EAAGX,EAAK,IAAKU,CAAI,CAAC,CACrC,CAEA,SAASC,EAAUC,EAAmBC,EAAc,CACnD,IAAMC,EAAWC,GAAqBA,EAAQ,KAAK,KAAMC,GAAMA,EAAE,OAAS,MAAM,EAC1EC,EACLJ,EAAI,OAAS,QAAUA,EAAI,OAAS,OACjCD,EACAC,EAAI,KACH,CAAC,GAAGD,EAAQC,EAAI,IAAI,EACpBD,EAGCM,EAAcJ,EAAQD,CAAG,EAAKJ,GAAqBD,EAAIS,EAASR,CAAM,EAAI,CAAC,EAEjF,QAAWU,KAASN,EAAI,KACnBM,EAAM,OAAS,OACdA,EAAM,KAAK,SAAW,EACzBD,EAAOC,EAAM,IAAI,EAAKC,GAAoBX,GACzCD,EAAI,CAAC,GAAGS,EAASG,CAAK,EAAGX,CAAM,EAEhCS,EAAOC,EAAM,IAAI,EAAKC,GAAmB,CAGxC,IAAMC,EAAUV,EAAU,CAAC,GAAGM,EAASG,CAAK,EAAGD,CAAK,EAGpD,OAAO,OAAO,OACbL,EAAQK,CAAK,EACTV,GAAqBD,EAAI,CAAC,GAAGS,EAASG,CAAK,EAAGX,CAAM,EACrD,CAAC,EACJY,CACD,CACD,EAESF,EAAM,OAAS,OACrBA,EAAM,KAAK,SAAW,EACzBD,EAAOC,EAAM,IAAI,EAAKV,GAAqBD,EAAI,CAAC,GAAGS,EAASE,EAAM,IAAI,EAAGV,CAAM,EAE/ES,EAAOC,EAAM,IAAI,EAAIR,EAAUM,EAASE,CAAK,EAEpCA,EAAM,OAAS,SACzBD,EAAOC,EAAM,IAAI,EAAKG,GAAiB,CAEtC,IAAMC,EADUJ,EAAM,KAAKG,CAAG,EACJ,CAAC,GAAGL,EAASE,EAAM,IAAI,EAAIF,EAC/CI,EAAUV,EAAUY,EAASJ,CAAY,EAE/C,OAAO,OAAO,OAEbL,EAAQK,CAAY,EAAKV,GAAqBD,EAAIe,EAASd,CAAM,EAAI,CAAC,EACtEY,CACD,CACD,GAIF,OAAOG,EAAkBN,EAAQD,EAASJ,EAAI,IAAI,CACnD,CAEA,SAASW,EAAkBN,EAAaO,EAAqBnB,EAA0B,CACtF,OAAAY,EAAO,KAAO,CAACQ,EAAeC,IAGtBhB,EACNe,EAAO,CAAC,GAAGD,EAAU,GAAI,MAAM,QAAQE,CAAG,EAAIA,EAAM,CAACA,CAAG,CAAE,EAAIF,EAC9DzB,EAAK,GAAIM,CAAI,CACd,EAEDY,EAAO,KAAQS,GACPhB,EAAU,CAAC,GAAGc,EAAU,GAAI,MAAM,QAAQE,CAAG,EAAIA,EAAM,CAACA,CAAG,CAAE,EAAG3B,EAAK,GAAIM,CAAI,CAAC,EAG/EY,CACR","names":["api_exports","__export","keep","path","root","slot","wrap","__toCommonJS","name","rest","when","url","search","defs","buildPath","prefix","def","hasKeep","pathDef","c","allPath","target","child","param","subTree","arg","wrapped","attachWhenAndJoin","basePath","cond","seg"]}
1
+ {"version":3,"sources":["../src/api.ts"],"sourcesContent":["import {\n\tKeep,\n\tPath,\n\tPathDef,\n\tPick,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tToCamelCase,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamelCase = <S extends string>(s: S) =>\n\ts\n\t\t.replace(/^-+/, '')\n\t\t.replace(/-+$/, '')\n\t\t.replace(/^_+/, '')\n\t\t.replace(/_+$/, '')\n\t\t.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\n\t\t.replace(/_([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()) as ToCamelCase<S>\n\nconst IDENT = /^[A-Za-z_-][A-Za-z0-9_-]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap' | 'pick',\n\tname: Name\n): Name {\n\t// Allow your synthetic node(\"\") only for nodes (internal)\n\tif (kind === 'path' && name === '') return name.trim() as Name\n\n\tif (!name) throw new Error(`${kind} name cannot be empty`)\n\tif (!IDENT.test(name)) {\n\t\tthrow new Error(\n\t\t\t`${kind} name \"${name}\" must be a valid identifier (letters/digits/_/$, not starting with a digit).`\n\t\t)\n\t}\n\n\treturn name.trim() as Name\n}\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Path<Name, ToCamelCase<Name>, List> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamelCase(name),\n\tlist: (list ?? []) as List,\n})\n\nexport const slot = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Slot<Name, `$${ToCamelCase<Name>}`, List> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tlist: (list ?? []) as List,\n})\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\tlist?: List\n): Wrap<Name, `$${ToCamelCase<Name>}`, List, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: `$${toCamelCase(name)}`,\n\twhen,\n\tlist: (list ?? []) as List,\n})\n\nexport const pick = <\n\tconst Name extends string,\n\tconst Mode extends Record<string, readonly Segment[]>,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tmode: Mode,\n\tlist?: List\n): Pick<Name, `$${ToCamelCase<Name>}`, Mode, List> => ({\n\tkind: 'pick',\n\tname: assertValidName('pick', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tmode,\n\tlist: (list ?? []) as List,\n})\n\nexport const root = <const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs> =>\n\tbuildNode([], path('', defs)) as unknown as RoutesFromDefs<Defs>\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path\n\t\t.filter(Boolean)\n\t\t.join('/')\n\t\t.replace(/\\/{2,}/g, '/')}${search ? `?${search}` : ''}`\n\nfunction buildNode(prefix: Segment[], parent: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.list.some((node: any) => node.kind === 'keep')\n\tconst allPath = parent.kind === 'path' && parent.name ? [...prefix, parent.name] : prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = makeTarget(hasKeep(parent), allPath)\n\n\tfor (const child of parent.list) {\n\t\tif (child.kind !== 'keep') {\n\t\t\tif (child.uuid in target) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Duplicate uuid \"${String(child.uuid)}\" under \"${allPath.join('/') || '/'}\"`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (child.kind === 'slot') {\n\t\t\ttarget[child.uuid] = function bind(param: Segment) {\n\t\t\t\tconst next = [...allPath, param]\n\n\t\t\t\t// leaf slot => callable endpoint directly\n\t\t\t\tif (child.list.length === 0) {\n\t\t\t\t\treturn attachWhenAndJoin(makeTarget(true, next), next, [])\n\t\t\t\t}\n\n\t\t\t\t// non-leaf => subtree (optionally callable if keep())\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), next), buildNode(next, child))\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.list.length === 0) {\n\t\t\t\tconst leafPath = [...allPath, child.name]\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(makeTarget(true, leafPath), leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildNode(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = function wrap(arg: unknown) {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildNode(wrapped, child)\n\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), wrapped), subTree)\n\t\t\t}\n\t\t} else if (child.kind === 'pick') {\n\t\t\ttarget[child.uuid] = (value: keyof typeof child.mode) => {\n\t\t\t\tif (child.mode[value]) {\n\t\t\t\t\treturn buildNode([...allPath, ...child.mode[value]], path('', child.list))\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`pick(\"${child.name}\") got unknown value: ${String(value)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, parent.list)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], list: readonly PathDef[]) {\n\tconst when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\tconst nextPath = cond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath\n\n\t\t// If this is a callable leaf (no children), preserve callability after .when().\n\t\tif (list.length === 0 && typeof target === 'function') {\n\t\t\treturn attachWhenAndJoin(makeTarget(true, nextPath), nextPath, list)\n\t\t}\n\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildNode(nextPath, path('', list))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = function join(seg: Segment | readonly Segment[]) {\n\t\treturn when(true, seg)\n\t}\n\n\treturn target\n}\n\nconst makeTarget = (callable: boolean, currentPath: Segment[]) => {\n\treturn callable ? (search?: SParams) => url(currentPath, search) : Object.create(null)\n}\n"],"mappings":"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","names":["api_exports","__export","keep","path","pick","root","slot","wrap","__toCommonJS","toCamelCase","s","_","c","IDENT","assertValidName","kind","name","list","when","mode","defs","buildNode","url","search","prefix","parent","hasKeep","pathDef","node","allPath","target","makeTarget","child","param","next","attachWhenAndJoin","leafPath","arg","wrapped","subTree","value","basePath","cond","seg","nextPath","callable","currentPath"]}
package/dist/api.d.cts CHANGED
@@ -1,3 +1,11 @@
1
+ // ---------- Helpers ----------------------
2
+ type Delimiter = '_' | '-'
3
+ type ToCamelCase<S extends string> = S extends `${infer A}${Delimiter}${infer B}`
4
+ ? A extends ''
5
+ ? ToCamelCase<B>
6
+ : `${A}${Capitalize<ToCamelCase<B>>}`
7
+ : S
8
+
1
9
  // ---------- Shared public types ----------
2
10
  type Segment = string | number
3
11
  type SParams = string | URLSearchParams
@@ -7,90 +15,85 @@ type Keep = { kind: 'keep' }
7
15
 
8
16
  type Path<
9
17
  Name extends string = string,
10
- Rest extends readonly PathDef[] = readonly PathDef[],
11
- > = { kind: 'path'; name: Name; rest: Rest }
18
+ Uuid extends string = string,
19
+ List extends readonly PathDef[] = readonly PathDef[],
20
+ > = { kind: 'path'; name: Name; uuid: Uuid; list: List }
12
21
 
13
22
  type Slot<
14
23
  Name extends string = string,
15
- Rest extends readonly PathDef[] = readonly PathDef[],
16
- > = { kind: 'slot'; name: Name; rest: Rest }
24
+ Uuid extends string = string,
25
+ List extends readonly PathDef[] = readonly PathDef[],
26
+ > = { kind: 'slot'; name: Name; uuid: Uuid; list: List }
17
27
 
18
28
  type Wrap<
19
29
  Name extends string = string,
20
- Rest extends readonly PathDef[] = readonly PathDef[],
30
+ Uuid extends string = string,
31
+ List extends readonly PathDef[] = readonly PathDef[],
21
32
  Args = unknown,
22
- > = { kind: 'wrap'; name: Name; rest: Rest; when: (args: Args) => boolean }
33
+ > = { kind: 'wrap'; name: Name; uuid: Uuid; list: List; when: (args: Args) => boolean }
23
34
 
24
- type SlotDef =
25
- | Path<string, readonly PathDef[]>
26
- | Slot<string, readonly PathDef[]>
27
- | Wrap<string, readonly PathDef[], any>
28
- type PathDef = SlotDef | Keep
35
+ type Pick<
36
+ Name extends string = string,
37
+ Uuid extends string = string,
38
+ Mode extends Record<string, readonly Segment[]> = Record<string, readonly Segment[]>,
39
+ List extends readonly PathDef[] = readonly PathDef[],
40
+ > = { kind: 'pick'; name: Name; uuid: Uuid; mode: Mode; list: List }
41
+ type PathDef = Path | Slot | Wrap | Pick | Keep
29
42
 
30
43
  // ---------- Type-level route builder ----------
31
- interface Whenable {
32
- when(cond: boolean, seg: Segment | readonly Segment[]): this
33
- join(seg: Segment | readonly Segment[]): this
44
+ type PickKey<M> = Extract<keyof M, string>
45
+ type List<Defs extends readonly PathDef[]> = Defs[number]
46
+ type CallIfKeep<U, Props> =
47
+ Extract<U, Keep> extends never ? Props : ((search?: SParams) => string) & Props
48
+ type WithWhen<T> = T & {
49
+ $when(cond: boolean, seg: Segment | readonly Segment[]): this
50
+ $join(seg: Segment | readonly Segment[]): this
34
51
  }
35
52
 
36
- type HasKeep<Rest extends readonly PathDef[]> =
37
- Extract<Rest[number], Keep> extends never ? false : true
38
-
39
- type NonKeepChildren<Rest extends readonly PathDef[]> = Exclude<Rest[number], Keep>
40
-
41
- type PropsFromChildren<Rest extends readonly PathDef[]> = {
42
- [C in NonKeepChildren<Rest> as C extends { name: infer N extends string }
43
- ? N
44
- : never]: C extends Path<any, any>
53
+ type PropsFromChildren<Defs extends readonly PathDef[]> = {
54
+ [C in Exclude<List<Defs>, Keep> as C extends { uuid: infer N extends string }
55
+ ? ToCamelCase<N>
56
+ : never]: C extends Path<any, any, any>
45
57
  ? RouteFromPath<C>
46
- : C extends Slot<any, any>
58
+ : C extends Slot<any, any, any>
47
59
  ? RouteFromSlot<C>
48
- : C extends Wrap<any, any, any>
60
+ : C extends Wrap<any, any, any, any>
49
61
  ? RouteFromWrap<C>
50
- : never
62
+ : C extends Pick<any, any, any, any>
63
+ ? RouteFromPick<C>
64
+ : never
51
65
  }
52
66
 
53
- type WithWhen<T> = T & Whenable
54
-
55
- // Example: apply it to the outputs
56
- type RouteFromPath<N extends Path<any, any>> = WithWhen<
57
- N['rest'] extends readonly []
67
+ type RouteFromPath<Node extends Path<any, any, any>> = WithWhen<
68
+ Node['list'] extends readonly []
58
69
  ? (search?: SParams) => string
59
- : HasKeep<N['rest']> extends true
60
- ? ((search?: SParams) => string) & PropsFromChildren<N['rest']>
61
- : PropsFromChildren<N['rest']>
70
+ : CallIfKeep<List<Node['list']>, PropsFromChildren<Node['list']>>
62
71
  >
63
72
 
64
- type SlotResult<Rest extends readonly PathDef[]> = WithWhen<
65
- Rest extends readonly []
73
+ type RouteFromSlot<Node extends Slot<any, any, any>> = (param: Segment) => SlotResult<Node['list']>
74
+ type SlotResult<Defs extends readonly PathDef[]> = WithWhen<
75
+ Defs extends readonly []
66
76
  ? (search?: SParams) => string
67
- : HasKeep<Rest> extends true
68
- ? ((search?: SParams) => string) & PropsFromChildren<Rest>
69
- : PropsFromChildren<Rest>
77
+ : CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
70
78
  >
71
79
 
72
- type RouteFromSlot<I extends Slot<any, any>> = (param: Segment) => SlotResult<I['rest']>
73
-
74
- type WrapArg<W extends Wrap<any, any, any>> = Parameters<W['when']>[0]
75
-
76
- type WrapResult<Rest extends readonly PathDef[]> = WithWhen<
77
- HasKeep<Rest> extends true
78
- ? ((search?: SParams) => string) & PropsFromChildren<Rest>
79
- : PropsFromChildren<Rest>
80
- >
80
+ type RouteFromWrap<W extends Wrap<any, any, any, any>> = (
81
+ arg: Parameters<W['when']>[0]
82
+ ) => WithWhen<CallIfKeep<List<W['list']>, PropsFromChildren<W['list']>>>
81
83
 
82
- type RouteFromWrap<W extends Wrap<any, any, any>> = (arg: WrapArg<W>) => WrapResult<W['rest']>
84
+ type RouteFromPick<P extends Pick<any, any, any, any>> = (
85
+ val: PickKey<P['mode']>
86
+ ) => RoutesFromDefs<P['list']>
83
87
 
84
88
  type RoutesFromDefs<Defs extends readonly PathDef[]> = WithWhen<
85
- HasKeep<Defs> extends true
86
- ? ((search?: SParams) => string) & PropsFromChildren<Defs>
87
- : PropsFromChildren<Defs>
89
+ CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
88
90
  >
89
91
 
90
92
  declare const keep: () => Keep;
91
- declare const path: <const Name extends string, const Rest extends readonly PathDef[] = readonly []>(name: Name, rest?: Rest) => Path<Name, Rest>;
92
- declare const slot: <const Name extends string, const Rest extends readonly PathDef[] = readonly []>(name: Name, rest?: Rest) => Slot<Name, Rest>;
93
- declare const wrap: <const Name extends string, const Rest extends readonly PathDef[] = readonly [], Args = unknown>(name: Name, when: (args: Args) => boolean, rest?: Rest) => Wrap<Name, Rest, Args>;
94
- declare function root<const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs>;
93
+ declare const path: <const Name extends string, const List extends readonly PathDef[] = readonly []>(name: Name, list?: List) => Path<Name, ToCamelCase<Name>, List>;
94
+ declare const slot: <const Name extends string, const List extends readonly PathDef[] = readonly []>(name: Name, list?: List) => Slot<Name, `$${ToCamelCase<Name>}`, List>;
95
+ declare const wrap: <const Name extends string, const List extends readonly PathDef[] = readonly [], Args = unknown>(name: Name, when: (args: Args) => boolean, list?: List) => Wrap<Name, `$${ToCamelCase<Name>}`, List, Args>;
96
+ declare const pick: <const Name extends string, const Mode extends Record<string, readonly Segment[]>, const List extends readonly PathDef[] = readonly []>(name: Name, mode: Mode, list?: List) => Pick<Name, `$${ToCamelCase<Name>}`, Mode, List>;
97
+ declare const root: <const Defs extends readonly PathDef[]>(defs: Defs) => RoutesFromDefs<Defs>;
95
98
 
96
- export { keep, path, root, slot, wrap };
99
+ export { keep, path, pick, root, slot, wrap };
package/dist/api.d.ts CHANGED
@@ -1,3 +1,11 @@
1
+ // ---------- Helpers ----------------------
2
+ type Delimiter = '_' | '-'
3
+ type ToCamelCase<S extends string> = S extends `${infer A}${Delimiter}${infer B}`
4
+ ? A extends ''
5
+ ? ToCamelCase<B>
6
+ : `${A}${Capitalize<ToCamelCase<B>>}`
7
+ : S
8
+
1
9
  // ---------- Shared public types ----------
2
10
  type Segment = string | number
3
11
  type SParams = string | URLSearchParams
@@ -7,90 +15,85 @@ type Keep = { kind: 'keep' }
7
15
 
8
16
  type Path<
9
17
  Name extends string = string,
10
- Rest extends readonly PathDef[] = readonly PathDef[],
11
- > = { kind: 'path'; name: Name; rest: Rest }
18
+ Uuid extends string = string,
19
+ List extends readonly PathDef[] = readonly PathDef[],
20
+ > = { kind: 'path'; name: Name; uuid: Uuid; list: List }
12
21
 
13
22
  type Slot<
14
23
  Name extends string = string,
15
- Rest extends readonly PathDef[] = readonly PathDef[],
16
- > = { kind: 'slot'; name: Name; rest: Rest }
24
+ Uuid extends string = string,
25
+ List extends readonly PathDef[] = readonly PathDef[],
26
+ > = { kind: 'slot'; name: Name; uuid: Uuid; list: List }
17
27
 
18
28
  type Wrap<
19
29
  Name extends string = string,
20
- Rest extends readonly PathDef[] = readonly PathDef[],
30
+ Uuid extends string = string,
31
+ List extends readonly PathDef[] = readonly PathDef[],
21
32
  Args = unknown,
22
- > = { kind: 'wrap'; name: Name; rest: Rest; when: (args: Args) => boolean }
33
+ > = { kind: 'wrap'; name: Name; uuid: Uuid; list: List; when: (args: Args) => boolean }
23
34
 
24
- type SlotDef =
25
- | Path<string, readonly PathDef[]>
26
- | Slot<string, readonly PathDef[]>
27
- | Wrap<string, readonly PathDef[], any>
28
- type PathDef = SlotDef | Keep
35
+ type Pick<
36
+ Name extends string = string,
37
+ Uuid extends string = string,
38
+ Mode extends Record<string, readonly Segment[]> = Record<string, readonly Segment[]>,
39
+ List extends readonly PathDef[] = readonly PathDef[],
40
+ > = { kind: 'pick'; name: Name; uuid: Uuid; mode: Mode; list: List }
41
+ type PathDef = Path | Slot | Wrap | Pick | Keep
29
42
 
30
43
  // ---------- Type-level route builder ----------
31
- interface Whenable {
32
- when(cond: boolean, seg: Segment | readonly Segment[]): this
33
- join(seg: Segment | readonly Segment[]): this
44
+ type PickKey<M> = Extract<keyof M, string>
45
+ type List<Defs extends readonly PathDef[]> = Defs[number]
46
+ type CallIfKeep<U, Props> =
47
+ Extract<U, Keep> extends never ? Props : ((search?: SParams) => string) & Props
48
+ type WithWhen<T> = T & {
49
+ $when(cond: boolean, seg: Segment | readonly Segment[]): this
50
+ $join(seg: Segment | readonly Segment[]): this
34
51
  }
35
52
 
36
- type HasKeep<Rest extends readonly PathDef[]> =
37
- Extract<Rest[number], Keep> extends never ? false : true
38
-
39
- type NonKeepChildren<Rest extends readonly PathDef[]> = Exclude<Rest[number], Keep>
40
-
41
- type PropsFromChildren<Rest extends readonly PathDef[]> = {
42
- [C in NonKeepChildren<Rest> as C extends { name: infer N extends string }
43
- ? N
44
- : never]: C extends Path<any, any>
53
+ type PropsFromChildren<Defs extends readonly PathDef[]> = {
54
+ [C in Exclude<List<Defs>, Keep> as C extends { uuid: infer N extends string }
55
+ ? ToCamelCase<N>
56
+ : never]: C extends Path<any, any, any>
45
57
  ? RouteFromPath<C>
46
- : C extends Slot<any, any>
58
+ : C extends Slot<any, any, any>
47
59
  ? RouteFromSlot<C>
48
- : C extends Wrap<any, any, any>
60
+ : C extends Wrap<any, any, any, any>
49
61
  ? RouteFromWrap<C>
50
- : never
62
+ : C extends Pick<any, any, any, any>
63
+ ? RouteFromPick<C>
64
+ : never
51
65
  }
52
66
 
53
- type WithWhen<T> = T & Whenable
54
-
55
- // Example: apply it to the outputs
56
- type RouteFromPath<N extends Path<any, any>> = WithWhen<
57
- N['rest'] extends readonly []
67
+ type RouteFromPath<Node extends Path<any, any, any>> = WithWhen<
68
+ Node['list'] extends readonly []
58
69
  ? (search?: SParams) => string
59
- : HasKeep<N['rest']> extends true
60
- ? ((search?: SParams) => string) & PropsFromChildren<N['rest']>
61
- : PropsFromChildren<N['rest']>
70
+ : CallIfKeep<List<Node['list']>, PropsFromChildren<Node['list']>>
62
71
  >
63
72
 
64
- type SlotResult<Rest extends readonly PathDef[]> = WithWhen<
65
- Rest extends readonly []
73
+ type RouteFromSlot<Node extends Slot<any, any, any>> = (param: Segment) => SlotResult<Node['list']>
74
+ type SlotResult<Defs extends readonly PathDef[]> = WithWhen<
75
+ Defs extends readonly []
66
76
  ? (search?: SParams) => string
67
- : HasKeep<Rest> extends true
68
- ? ((search?: SParams) => string) & PropsFromChildren<Rest>
69
- : PropsFromChildren<Rest>
77
+ : CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
70
78
  >
71
79
 
72
- type RouteFromSlot<I extends Slot<any, any>> = (param: Segment) => SlotResult<I['rest']>
73
-
74
- type WrapArg<W extends Wrap<any, any, any>> = Parameters<W['when']>[0]
75
-
76
- type WrapResult<Rest extends readonly PathDef[]> = WithWhen<
77
- HasKeep<Rest> extends true
78
- ? ((search?: SParams) => string) & PropsFromChildren<Rest>
79
- : PropsFromChildren<Rest>
80
- >
80
+ type RouteFromWrap<W extends Wrap<any, any, any, any>> = (
81
+ arg: Parameters<W['when']>[0]
82
+ ) => WithWhen<CallIfKeep<List<W['list']>, PropsFromChildren<W['list']>>>
81
83
 
82
- type RouteFromWrap<W extends Wrap<any, any, any>> = (arg: WrapArg<W>) => WrapResult<W['rest']>
84
+ type RouteFromPick<P extends Pick<any, any, any, any>> = (
85
+ val: PickKey<P['mode']>
86
+ ) => RoutesFromDefs<P['list']>
83
87
 
84
88
  type RoutesFromDefs<Defs extends readonly PathDef[]> = WithWhen<
85
- HasKeep<Defs> extends true
86
- ? ((search?: SParams) => string) & PropsFromChildren<Defs>
87
- : PropsFromChildren<Defs>
89
+ CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
88
90
  >
89
91
 
90
92
  declare const keep: () => Keep;
91
- declare const path: <const Name extends string, const Rest extends readonly PathDef[] = readonly []>(name: Name, rest?: Rest) => Path<Name, Rest>;
92
- declare const slot: <const Name extends string, const Rest extends readonly PathDef[] = readonly []>(name: Name, rest?: Rest) => Slot<Name, Rest>;
93
- declare const wrap: <const Name extends string, const Rest extends readonly PathDef[] = readonly [], Args = unknown>(name: Name, when: (args: Args) => boolean, rest?: Rest) => Wrap<Name, Rest, Args>;
94
- declare function root<const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs>;
93
+ declare const path: <const Name extends string, const List extends readonly PathDef[] = readonly []>(name: Name, list?: List) => Path<Name, ToCamelCase<Name>, List>;
94
+ declare const slot: <const Name extends string, const List extends readonly PathDef[] = readonly []>(name: Name, list?: List) => Slot<Name, `$${ToCamelCase<Name>}`, List>;
95
+ declare const wrap: <const Name extends string, const List extends readonly PathDef[] = readonly [], Args = unknown>(name: Name, when: (args: Args) => boolean, list?: List) => Wrap<Name, `$${ToCamelCase<Name>}`, List, Args>;
96
+ declare const pick: <const Name extends string, const Mode extends Record<string, readonly Segment[]>, const List extends readonly PathDef[] = readonly []>(name: Name, mode: Mode, list?: List) => Pick<Name, `$${ToCamelCase<Name>}`, Mode, List>;
97
+ declare const root: <const Defs extends readonly PathDef[]>(defs: Defs) => RoutesFromDefs<Defs>;
95
98
 
96
- export { keep, path, root, slot, wrap };
99
+ export { keep, path, pick, root, slot, wrap };
package/dist/api.js CHANGED
@@ -1,2 +1,2 @@
1
- var u=()=>({kind:"keep"}),d=(t,n)=>({kind:"path",name:t,rest:n??[]}),h=(t,n)=>({kind:"slot",name:t,rest:n??[]}),y=(t,n,r)=>({kind:"wrap",name:t,when:n,rest:r??[]}),l=(t,n)=>`${t.filter(Boolean).join("/")}${n?`?${n}`:""}`.replace("//","/");function g(t){return m([],d("/",t))}function m(t,n){let r=e=>e.rest.some(o=>o.kind==="keep"),s=n.kind==="slot"||n.kind==="wrap"?t:n.name?[...t,n.name]:t,a=r(n)?e=>l(s,e):{};for(let e of n.rest)e.kind==="slot"?e.rest.length===0?a[e.name]=o=>i=>l([...s,o],i):a[e.name]=o=>{let i=m([...s,o],e);return Object.assign(r(e)?c=>l([...s,o],c):{},i)}:e.kind==="path"?e.rest.length===0?a[e.name]=o=>l([...s,e.name],o):a[e.name]=m(s,e):e.kind==="wrap"&&(a[e.name]=o=>{let c=e.when(o)?[...s,e.name]:s,S=m(c,e);return Object.assign(r(e)?p=>l(c,p):{},S)});return f(a,s,n.rest)}function f(t,n,r){return t.when=(s,a)=>m(s?[...n,...Array.isArray(a)?a:[a]]:n,d("",r)),t.join=s=>m([...n,...Array.isArray(s)?s:[s]],d("",r)),t}export{u as keep,d as path,g as root,h as slot,y as wrap};
1
+ import{a,b,c,d,e,f}from"./chunk-TB22YO7J.js";export{a as keep,b as path,e as pick,f as root,c as slot,d as wrap};
2
2
  //# sourceMappingURL=api.js.map
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api.ts"],"sourcesContent":["import { Keep, Path, Wrap, Slot, PathDef, SlotDef, Segment, SParams, RoutesFromDefs } from './dsl'\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst Rest extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\trest?: Rest\n): Path<Name, Rest> => ({ kind: 'path', name, rest: (rest ?? []) as Rest })\n\nexport const slot = <\n\tconst Name extends string,\n\tconst Rest extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\trest?: Rest\n): Slot<Name, Rest> => ({ kind: 'slot', name, rest: (rest ?? []) as Rest })\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst Rest extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\trest?: Rest\n): Wrap<Name, Rest, Args> => ({ kind: 'wrap', name, when, rest: (rest ?? []) as Rest })\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`${path.filter(Boolean).join('/')}${search ? `?${search}` : ''}`.replace('//', '/')\n\n// ---------- Typed root signature ----------\nexport function root<const Defs extends readonly PathDef[]>(\n\tdefs: Defs\n): RoutesFromDefs<Defs> {\n\treturn buildPath([], path('/', defs)) as unknown as RoutesFromDefs<Defs>\n}\n\nfunction buildPath(prefix: Segment[], def: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.rest.some((c) => c.kind === 'keep')\n\tconst allPath =\n\t\tdef.kind === 'slot' || def.kind === 'wrap'\n\t\t\t? prefix\n\t\t\t: def.name\n\t\t\t\t? [...prefix, def.name]\n\t\t\t\t: prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = hasKeep(def) ? (search?: SParams) => url(allPath, search) : {}\n\n\tfor (const child of def.rest) {\n\t\tif (child.kind === 'slot') {\n\t\t\tif (child.rest.length === 0) {\n\t\t\t\ttarget[child.name] = (param: Segment) => (search?: SParams) =>\n\t\t\t\t\turl([...allPath, param], search)\n\t\t\t} else {\n\t\t\t\ttarget[child.name] = (param: Segment) => {\n\t\t\t\t\t// Build subtree for nested parts under :id\n\t\t\t\t\t// Synthetic path with empty name so we don't add extra segment.\n\t\t\t\t\tconst subTree = buildPath([...allPath, param], child)\n\n\t\t\t\t\t// Attach children (info, activities, etc.) to that function\n\t\t\t\t\treturn Object.assign(\n\t\t\t\t\t\thasKeep(child)\n\t\t\t\t\t\t\t? (search?: SParams) => url([...allPath, param], search)\n\t\t\t\t\t\t\t: {},\n\t\t\t\t\t\tsubTree\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.rest.length === 0) {\n\t\t\t\ttarget[child.name] = (search?: SParams) => url([...allPath, child.name], search)\n\t\t\t} else {\n\t\t\t\ttarget[child.name] = buildPath(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.name] = (arg: unknown) => {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildPath(wrapped, child as any)\n\n\t\t\t\treturn Object.assign(\n\t\t\t\t\t// if wrap has keep(), it becomes callable at that point\n\t\t\t\t\thasKeep(child as any) ? (search?: SParams) => url(wrapped, search) : {},\n\t\t\t\t\tsubTree\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, def.rest)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], rest: readonly PathDef[]) {\n\ttarget.when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildPath(\n\t\t\tcond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath,\n\t\t\tpath('', rest)\n\t\t)\n\t}\n\ttarget.join = (seg: Segment | readonly Segment[]) => {\n\t\treturn buildPath([...basePath, ...(Array.isArray(seg) ? seg : [seg])], path('', rest))\n\t}\n\n\treturn target\n}\n"],"mappings":"AAGO,IAAMA,EAAO,KAAa,CAAE,KAAM,MAAO,GAEnCC,EAAO,CAInBC,EACAC,KACuB,CAAE,KAAM,OAAQ,KAAAD,EAAM,KAAOC,GAAQ,CAAC,CAAW,GAE5DC,EAAO,CAInBF,EACAC,KACuB,CAAE,KAAM,OAAQ,KAAAD,EAAM,KAAOC,GAAQ,CAAC,CAAW,GAE5DE,EAAO,CAKnBH,EACAI,EACAH,KAC6B,CAAE,KAAM,OAAQ,KAAAD,EAAM,KAAAI,EAAM,KAAOH,GAAQ,CAAC,CAAW,GAG/EI,EAAM,CAACN,EAAiBO,IAC7B,GAAGP,EAAK,OAAO,OAAO,EAAE,KAAK,GAAG,CAAC,GAAGO,EAAS,IAAIA,CAAM,GAAK,EAAE,GAAG,QAAQ,KAAM,GAAG,EAG5E,SAASC,EACfC,EACuB,CACvB,OAAOC,EAAU,CAAC,EAAGV,EAAK,IAAKS,CAAI,CAAC,CACrC,CAEA,SAASC,EAAUC,EAAmBC,EAAc,CACnD,IAAMC,EAAWC,GAAqBA,EAAQ,KAAK,KAAMC,GAAMA,EAAE,OAAS,MAAM,EAC1EC,EACLJ,EAAI,OAAS,QAAUA,EAAI,OAAS,OACjCD,EACAC,EAAI,KACH,CAAC,GAAGD,EAAQC,EAAI,IAAI,EACpBD,EAGCM,EAAcJ,EAAQD,CAAG,EAAKL,GAAqBD,EAAIU,EAAST,CAAM,EAAI,CAAC,EAEjF,QAAWW,KAASN,EAAI,KACnBM,EAAM,OAAS,OACdA,EAAM,KAAK,SAAW,EACzBD,EAAOC,EAAM,IAAI,EAAKC,GAAoBZ,GACzCD,EAAI,CAAC,GAAGU,EAASG,CAAK,EAAGZ,CAAM,EAEhCU,EAAOC,EAAM,IAAI,EAAKC,GAAmB,CAGxC,IAAMC,EAAUV,EAAU,CAAC,GAAGM,EAASG,CAAK,EAAGD,CAAK,EAGpD,OAAO,OAAO,OACbL,EAAQK,CAAK,EACTX,GAAqBD,EAAI,CAAC,GAAGU,EAASG,CAAK,EAAGZ,CAAM,EACrD,CAAC,EACJa,CACD,CACD,EAESF,EAAM,OAAS,OACrBA,EAAM,KAAK,SAAW,EACzBD,EAAOC,EAAM,IAAI,EAAKX,GAAqBD,EAAI,CAAC,GAAGU,EAASE,EAAM,IAAI,EAAGX,CAAM,EAE/EU,EAAOC,EAAM,IAAI,EAAIR,EAAUM,EAASE,CAAK,EAEpCA,EAAM,OAAS,SACzBD,EAAOC,EAAM,IAAI,EAAKG,GAAiB,CAEtC,IAAMC,EADUJ,EAAM,KAAKG,CAAG,EACJ,CAAC,GAAGL,EAASE,EAAM,IAAI,EAAIF,EAC/CI,EAAUV,EAAUY,EAASJ,CAAY,EAE/C,OAAO,OAAO,OAEbL,EAAQK,CAAY,EAAKX,GAAqBD,EAAIgB,EAASf,CAAM,EAAI,CAAC,EACtEa,CACD,CACD,GAIF,OAAOG,EAAkBN,EAAQD,EAASJ,EAAI,IAAI,CACnD,CAEA,SAASW,EAAkBN,EAAaO,EAAqBtB,EAA0B,CACtF,OAAAe,EAAO,KAAO,CAACQ,EAAeC,IAGtBhB,EACNe,EAAO,CAAC,GAAGD,EAAU,GAAI,MAAM,QAAQE,CAAG,EAAIA,EAAM,CAACA,CAAG,CAAE,EAAIF,EAC9DxB,EAAK,GAAIE,CAAI,CACd,EAEDe,EAAO,KAAQS,GACPhB,EAAU,CAAC,GAAGc,EAAU,GAAI,MAAM,QAAQE,CAAG,EAAIA,EAAM,CAACA,CAAG,CAAE,EAAG1B,EAAK,GAAIE,CAAI,CAAC,EAG/Ee,CACR","names":["keep","path","name","rest","slot","wrap","when","url","search","root","defs","buildPath","prefix","def","hasKeep","pathDef","c","allPath","target","child","param","subTree","arg","wrapped","attachWhenAndJoin","basePath","cond","seg"]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ var u=t=>t.replace(/^-+/,"").replace(/-+$/,"").replace(/^_+/,"").replace(/_+$/,"").replace(/-([a-zA-Z0-9])/g,(n,s)=>s.toUpperCase()).replace(/_([a-zA-Z0-9])/g,(n,s)=>s.toUpperCase()),k=/^[A-Za-z_-][A-Za-z0-9_-]*$/;function m(t,n){if(t==="path"&&n==="")return n.trim();if(!n)throw new Error(`${t} name cannot be empty`);if(!k.test(n))throw new Error(`${t} name "${n}" must be a valid identifier (letters/digits/_/$, not starting with a digit).`);return n.trim()}var N=()=>({kind:"keep"}),f=(t,n)=>({kind:"path",name:m("path",t),uuid:u(t),list:n??[]}),S=(t,n)=>({kind:"slot",name:m("slot",t),uuid:`$${u(t)}`,list:n??[]}),w=(t,n,s)=>({kind:"wrap",name:m("wrap",t),uuid:`$${u(t)}`,when:n,list:s??[]}),y=(t,n,s)=>({kind:"pick",name:m("pick",t),uuid:`$${u(t)}`,mode:n,list:s??[]}),x=t=>l([],f("",t)),$=(t,n)=>`/${t.filter(Boolean).join("/").replace(/\/{2,}/g,"/")}${n?`?${n}`:""}`;function l(t,n){let s=e=>e.list.some(o=>o.kind==="keep"),a=n.kind==="path"&&n.name?[...t,n.name]:t,r=i(s(n),a);for(let e of n.list){if(e.kind!=="keep"&&e.uuid in r)throw new Error(`Duplicate uuid "${String(e.uuid)}" under "${a.join("/")||"/"}"`);if(e.kind==="slot")r[e.uuid]=function(p){let d=[...a,p];return e.list.length===0?c(i(!0,d),d,[]):Object.assign(i(s(e),d),l(d,e))};else if(e.kind==="path")if(e.list.length===0){let o=[...a,e.name];r[e.uuid]=c(i(!0,o),o,[])}else r[e.uuid]=l(a,e);else e.kind==="wrap"?r[e.uuid]=function(p){let g=e.when(p)?[...a,e.name]:a,h=l(g,e);return Object.assign(i(s(e),g),h)}:e.kind==="pick"&&(r[e.uuid]=o=>{if(e.mode[o])return l([...a,...e.mode[o]],f("",e.list));throw new Error(`pick("${e.name}") got unknown value: ${String(o)}`)})}return c(r,a,n.list)}function c(t,n,s){let a=(r,e)=>{let o=r?[...n,...Array.isArray(e)?e:[e]]:n;return s.length===0&&typeof t=="function"?c(i(!0,o),o,s):l(o,f("",s))};return t.$when=a,t.$join=function(e){return a(!0,e)},t}var i=(t,n)=>t?s=>$(n,s):Object.create(null);export{N as a,f as b,S as c,w as d,y as e,x as f};
2
+ //# sourceMappingURL=chunk-TB22YO7J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api.ts"],"sourcesContent":["import {\n\tKeep,\n\tPath,\n\tPathDef,\n\tPick,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tToCamelCase,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamelCase = <S extends string>(s: S) =>\n\ts\n\t\t.replace(/^-+/, '')\n\t\t.replace(/-+$/, '')\n\t\t.replace(/^_+/, '')\n\t\t.replace(/_+$/, '')\n\t\t.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\n\t\t.replace(/_([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()) as ToCamelCase<S>\n\nconst IDENT = /^[A-Za-z_-][A-Za-z0-9_-]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap' | 'pick',\n\tname: Name\n): Name {\n\t// Allow your synthetic node(\"\") only for nodes (internal)\n\tif (kind === 'path' && name === '') return name.trim() as Name\n\n\tif (!name) throw new Error(`${kind} name cannot be empty`)\n\tif (!IDENT.test(name)) {\n\t\tthrow new Error(\n\t\t\t`${kind} name \"${name}\" must be a valid identifier (letters/digits/_/$, not starting with a digit).`\n\t\t)\n\t}\n\n\treturn name.trim() as Name\n}\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Path<Name, ToCamelCase<Name>, List> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamelCase(name),\n\tlist: (list ?? []) as List,\n})\n\nexport const slot = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Slot<Name, `$${ToCamelCase<Name>}`, List> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tlist: (list ?? []) as List,\n})\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\tlist?: List\n): Wrap<Name, `$${ToCamelCase<Name>}`, List, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: `$${toCamelCase(name)}`,\n\twhen,\n\tlist: (list ?? []) as List,\n})\n\nexport const pick = <\n\tconst Name extends string,\n\tconst Mode extends Record<string, readonly Segment[]>,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tmode: Mode,\n\tlist?: List\n): Pick<Name, `$${ToCamelCase<Name>}`, Mode, List> => ({\n\tkind: 'pick',\n\tname: assertValidName('pick', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tmode,\n\tlist: (list ?? []) as List,\n})\n\nexport const root = <const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs> =>\n\tbuildNode([], path('', defs)) as unknown as RoutesFromDefs<Defs>\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path\n\t\t.filter(Boolean)\n\t\t.join('/')\n\t\t.replace(/\\/{2,}/g, '/')}${search ? `?${search}` : ''}`\n\nfunction buildNode(prefix: Segment[], parent: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.list.some((node: any) => node.kind === 'keep')\n\tconst allPath = parent.kind === 'path' && parent.name ? [...prefix, parent.name] : prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = makeTarget(hasKeep(parent), allPath)\n\n\tfor (const child of parent.list) {\n\t\tif (child.kind !== 'keep') {\n\t\t\tif (child.uuid in target) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Duplicate uuid \"${String(child.uuid)}\" under \"${allPath.join('/') || '/'}\"`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (child.kind === 'slot') {\n\t\t\ttarget[child.uuid] = function bind(param: Segment) {\n\t\t\t\tconst next = [...allPath, param]\n\n\t\t\t\t// leaf slot => callable endpoint directly\n\t\t\t\tif (child.list.length === 0) {\n\t\t\t\t\treturn attachWhenAndJoin(makeTarget(true, next), next, [])\n\t\t\t\t}\n\n\t\t\t\t// non-leaf => subtree (optionally callable if keep())\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), next), buildNode(next, child))\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.list.length === 0) {\n\t\t\t\tconst leafPath = [...allPath, child.name]\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(makeTarget(true, leafPath), leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildNode(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = function wrap(arg: unknown) {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildNode(wrapped, child)\n\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), wrapped), subTree)\n\t\t\t}\n\t\t} else if (child.kind === 'pick') {\n\t\t\ttarget[child.uuid] = (value: keyof typeof child.mode) => {\n\t\t\t\tif (child.mode[value]) {\n\t\t\t\t\treturn buildNode([...allPath, ...child.mode[value]], path('', child.list))\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`pick(\"${child.name}\") got unknown value: ${String(value)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, parent.list)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], list: readonly PathDef[]) {\n\tconst when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\tconst nextPath = cond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath\n\n\t\t// If this is a callable leaf (no children), preserve callability after .when().\n\t\tif (list.length === 0 && typeof target === 'function') {\n\t\t\treturn attachWhenAndJoin(makeTarget(true, nextPath), nextPath, list)\n\t\t}\n\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildNode(nextPath, path('', list))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = function join(seg: Segment | readonly Segment[]) {\n\t\treturn when(true, seg)\n\t}\n\n\treturn target\n}\n\nconst makeTarget = (callable: boolean, currentPath: Segment[]) => {\n\treturn callable ? (search?: SParams) => url(currentPath, search) : Object.create(null)\n}\n"],"mappings":"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","names":["toCamelCase","s","_","c","IDENT","assertValidName","kind","name","keep","path","list","slot","wrap","when","pick","mode","root","defs","buildNode","url","search","prefix","parent","hasKeep","pathDef","node","allPath","target","makeTarget","child","param","next","attachWhenAndJoin","leafPath","arg","wrapped","subTree","value","basePath","cond","seg","nextPath","callable","currentPath"]}
@@ -0,0 +1,2 @@
1
+ "use strict";var h=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var L=(t,e)=>{for(var s in e)h(t,s,{get:e[s],enumerable:!0})},P=(t,e,s,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of D(e))!C.call(t,a)&&a!==s&&h(t,a,{get:()=>e[a],enumerable:!(o=x(e,a))||o.enumerable});return t};var b=t=>P(h({},"__esModule",{value:!0}),t);var _={};L(_,{base:()=>k,bind:()=>$,link:()=>S,mask:()=>w,node:()=>c,pick:()=>N});module.exports=b(_);var u=t=>t.replace(/^-+/,"").replace(/-+$/,"").replace(/^_+/,"").replace(/_+$/,"").replace(/-([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()).replace(/_([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()),A=/^[A-Za-z_-][A-Za-z0-9_-]*$/;function p(t,e){if(t==="path"&&e==="")return e.trim();if(!e)throw new Error(`${t} name cannot be empty`);if(!A.test(e))throw new Error(`${t} name "${e}" must be a valid identifier (letters/digits/_/$, not starting with a digit).`);return e.trim()}var k=()=>({kind:"keep"}),c=(t,e)=>({kind:"path",name:p("path",t),uuid:u(t),list:e??[]}),$=(t,e)=>({kind:"slot",name:p("slot",t),uuid:`$${u(t)}`,list:e??[]}),w=(t,e,s)=>({kind:"wrap",name:p("wrap",t),uuid:`$${u(t)}`,when:e,list:s??[]}),N=(t,e,s)=>({kind:"pick",name:p("pick",t),uuid:`$${u(t)}`,mode:e,list:s??[]}),S=t=>l([],c("",t)),T=(t,e)=>`/${t.filter(Boolean).join("/").replace(/\/{2,}/g,"/")}${e?`?${e}`:""}`;function l(t,e){let s=n=>n.list.some(r=>r.kind==="keep"),o=e.kind==="path"&&e.name?[...t,e.name]:t,a=i(s(e),o);for(let n of e.list){if(n.kind!=="keep"&&n.uuid in a)throw new Error(`Duplicate uuid "${String(n.uuid)}" under "${o.join("/")||"/"}"`);if(n.kind==="slot")a[n.uuid]=function(f){let d=[...o,f];return n.list.length===0?m(i(!0,d),d,[]):Object.assign(i(s(n),d),l(d,n))};else if(n.kind==="path")if(n.list.length===0){let r=[...o,n.name];a[n.uuid]=m(i(!0,r),r,[])}else a[n.uuid]=l(o,n);else n.kind==="wrap"?a[n.uuid]=function(f){let g=n.when(f)?[...o,n.name]:o,y=l(g,n);return Object.assign(i(s(n),g),y)}:n.kind==="pick"&&(a[n.uuid]=r=>{if(n.mode[r])return l([...o,...n.mode[r]],c("",n.list));throw new Error(`pick("${n.name}") got unknown value: ${String(r)}`)})}return m(a,o,e.list)}function m(t,e,s){let o=(a,n)=>{let r=a?[...e,...Array.isArray(n)?n:[n]]:e;return s.length===0&&typeof t=="function"?m(i(!0,r),r,s):l(r,c("",s))};return t.$when=o,t.$join=function(n){return o(!0,n)},t}var i=(t,e)=>t?s=>T(e,s):Object.create(null);0&&(module.exports={base,bind,link,mask,node,pick});
2
+ //# sourceMappingURL=dialect-node.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dialect-node.ts","../src/api.ts"],"sourcesContent":["export { root as link, path as node, slot as bind, keep as base, wrap as mask, pick } from './api'\n","import {\n\tKeep,\n\tPath,\n\tPathDef,\n\tPick,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tToCamelCase,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamelCase = <S extends string>(s: S) =>\n\ts\n\t\t.replace(/^-+/, '')\n\t\t.replace(/-+$/, '')\n\t\t.replace(/^_+/, '')\n\t\t.replace(/_+$/, '')\n\t\t.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\n\t\t.replace(/_([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()) as ToCamelCase<S>\n\nconst IDENT = /^[A-Za-z_-][A-Za-z0-9_-]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap' | 'pick',\n\tname: Name\n): Name {\n\t// Allow your synthetic node(\"\") only for nodes (internal)\n\tif (kind === 'path' && name === '') return name.trim() as Name\n\n\tif (!name) throw new Error(`${kind} name cannot be empty`)\n\tif (!IDENT.test(name)) {\n\t\tthrow new Error(\n\t\t\t`${kind} name \"${name}\" must be a valid identifier (letters/digits/_/$, not starting with a digit).`\n\t\t)\n\t}\n\n\treturn name.trim() as Name\n}\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Path<Name, ToCamelCase<Name>, List> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamelCase(name),\n\tlist: (list ?? []) as List,\n})\n\nexport const slot = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Slot<Name, `$${ToCamelCase<Name>}`, List> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tlist: (list ?? []) as List,\n})\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\tlist?: List\n): Wrap<Name, `$${ToCamelCase<Name>}`, List, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: `$${toCamelCase(name)}`,\n\twhen,\n\tlist: (list ?? []) as List,\n})\n\nexport const pick = <\n\tconst Name extends string,\n\tconst Mode extends Record<string, readonly Segment[]>,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tmode: Mode,\n\tlist?: List\n): Pick<Name, `$${ToCamelCase<Name>}`, Mode, List> => ({\n\tkind: 'pick',\n\tname: assertValidName('pick', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tmode,\n\tlist: (list ?? []) as List,\n})\n\nexport const root = <const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs> =>\n\tbuildNode([], path('', defs)) as unknown as RoutesFromDefs<Defs>\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path\n\t\t.filter(Boolean)\n\t\t.join('/')\n\t\t.replace(/\\/{2,}/g, '/')}${search ? `?${search}` : ''}`\n\nfunction buildNode(prefix: Segment[], parent: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.list.some((node: any) => node.kind === 'keep')\n\tconst allPath = parent.kind === 'path' && parent.name ? [...prefix, parent.name] : prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = makeTarget(hasKeep(parent), allPath)\n\n\tfor (const child of parent.list) {\n\t\tif (child.kind !== 'keep') {\n\t\t\tif (child.uuid in target) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Duplicate uuid \"${String(child.uuid)}\" under \"${allPath.join('/') || '/'}\"`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (child.kind === 'slot') {\n\t\t\ttarget[child.uuid] = function bind(param: Segment) {\n\t\t\t\tconst next = [...allPath, param]\n\n\t\t\t\t// leaf slot => callable endpoint directly\n\t\t\t\tif (child.list.length === 0) {\n\t\t\t\t\treturn attachWhenAndJoin(makeTarget(true, next), next, [])\n\t\t\t\t}\n\n\t\t\t\t// non-leaf => subtree (optionally callable if keep())\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), next), buildNode(next, child))\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.list.length === 0) {\n\t\t\t\tconst leafPath = [...allPath, child.name]\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(makeTarget(true, leafPath), leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildNode(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = function wrap(arg: unknown) {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildNode(wrapped, child)\n\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), wrapped), subTree)\n\t\t\t}\n\t\t} else if (child.kind === 'pick') {\n\t\t\ttarget[child.uuid] = (value: keyof typeof child.mode) => {\n\t\t\t\tif (child.mode[value]) {\n\t\t\t\t\treturn buildNode([...allPath, ...child.mode[value]], path('', child.list))\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`pick(\"${child.name}\") got unknown value: ${String(value)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, parent.list)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], list: readonly PathDef[]) {\n\tconst when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\tconst nextPath = cond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath\n\n\t\t// If this is a callable leaf (no children), preserve callability after .when().\n\t\tif (list.length === 0 && typeof target === 'function') {\n\t\t\treturn attachWhenAndJoin(makeTarget(true, nextPath), nextPath, list)\n\t\t}\n\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildNode(nextPath, path('', list))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = function join(seg: Segment | readonly Segment[]) {\n\t\treturn when(true, seg)\n\t}\n\n\treturn target\n}\n\nconst makeTarget = (callable: boolean, currentPath: Segment[]) => {\n\treturn callable ? (search?: SParams) => url(currentPath, search) : Object.create(null)\n}\n"],"mappings":"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","names":["dialect_node_exports","__export","keep","slot","root","wrap","path","pick","__toCommonJS","toCamelCase","s","_","c","IDENT","assertValidName","kind","name","keep","path","list","slot","wrap","when","pick","mode","root","defs","buildNode","url","search","prefix","parent","hasKeep","pathDef","node","allPath","target","makeTarget","child","param","next","attachWhenAndJoin","leafPath","arg","wrapped","subTree","value","basePath","cond","seg","nextPath","callable","currentPath"]}
@@ -0,0 +1 @@
1
+ export { keep as base, slot as bind, root as link, wrap as mask, path as node, pick } from './api.cjs';
@@ -0,0 +1 @@
1
+ export { keep as base, slot as bind, root as link, wrap as mask, path as node, pick } from './api.js';
@@ -0,0 +1,2 @@
1
+ import{a,b as s,c as o,d as e,e as p,f as k}from"./chunk-TB22YO7J.js";export{a as base,o as bind,k as link,e as mask,s as node,p as pick};
2
+ //# sourceMappingURL=dialect-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ "use strict";var h=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var L=(t,e)=>{for(var o in e)h(t,o,{get:e[o],enumerable:!0})},P=(t,e,o,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of D(e))!C.call(t,s)&&s!==o&&h(t,s,{get:()=>e[s],enumerable:!(a=x(e,s))||a.enumerable});return t};var b=t=>P(h({},"__esModule",{value:!0}),t);var _={};L(_,{keep:()=>k,path:()=>c,pick:()=>N,root:()=>S,slot:()=>$,wrap:()=>w});module.exports=b(_);var u=t=>t.replace(/^-+/,"").replace(/-+$/,"").replace(/^_+/,"").replace(/_+$/,"").replace(/-([a-zA-Z0-9])/g,(e,o)=>o.toUpperCase()).replace(/_([a-zA-Z0-9])/g,(e,o)=>o.toUpperCase()),A=/^[A-Za-z_-][A-Za-z0-9_-]*$/;function p(t,e){if(t==="path"&&e==="")return e.trim();if(!e)throw new Error(`${t} name cannot be empty`);if(!A.test(e))throw new Error(`${t} name "${e}" must be a valid identifier (letters/digits/_/$, not starting with a digit).`);return e.trim()}var k=()=>({kind:"keep"}),c=(t,e)=>({kind:"path",name:p("path",t),uuid:u(t),list:e??[]}),$=(t,e)=>({kind:"slot",name:p("slot",t),uuid:`$${u(t)}`,list:e??[]}),w=(t,e,o)=>({kind:"wrap",name:p("wrap",t),uuid:`$${u(t)}`,when:e,list:o??[]}),N=(t,e,o)=>({kind:"pick",name:p("pick",t),uuid:`$${u(t)}`,mode:e,list:o??[]}),S=t=>l([],c("",t)),T=(t,e)=>`/${t.filter(Boolean).join("/").replace(/\/{2,}/g,"/")}${e?`?${e}`:""}`;function l(t,e){let o=n=>n.list.some(r=>r.kind==="keep"),a=e.kind==="path"&&e.name?[...t,e.name]:t,s=i(o(e),a);for(let n of e.list){if(n.kind!=="keep"&&n.uuid in s)throw new Error(`Duplicate uuid "${String(n.uuid)}" under "${a.join("/")||"/"}"`);if(n.kind==="slot")s[n.uuid]=function(f){let d=[...a,f];return n.list.length===0?m(i(!0,d),d,[]):Object.assign(i(o(n),d),l(d,n))};else if(n.kind==="path")if(n.list.length===0){let r=[...a,n.name];s[n.uuid]=m(i(!0,r),r,[])}else s[n.uuid]=l(a,n);else n.kind==="wrap"?s[n.uuid]=function(f){let g=n.when(f)?[...a,n.name]:a,y=l(g,n);return Object.assign(i(o(n),g),y)}:n.kind==="pick"&&(s[n.uuid]=r=>{if(n.mode[r])return l([...a,...n.mode[r]],c("",n.list));throw new Error(`pick("${n.name}") got unknown value: ${String(r)}`)})}return m(s,a,e.list)}function m(t,e,o){let a=(s,n)=>{let r=s?[...e,...Array.isArray(n)?n:[n]]:e;return o.length===0&&typeof t=="function"?m(i(!0,r),r,o):l(r,c("",o))};return t.$when=a,t.$join=function(n){return a(!0,n)},t}var i=(t,e)=>t?o=>T(e,o):Object.create(null);0&&(module.exports={keep,path,pick,root,slot,wrap});
2
+ //# sourceMappingURL=dialect-path.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dialect-path.ts","../src/api.ts"],"sourcesContent":["export { root, path, slot, keep, wrap, pick } from './api'\n","import {\n\tKeep,\n\tPath,\n\tPathDef,\n\tPick,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tToCamelCase,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamelCase = <S extends string>(s: S) =>\n\ts\n\t\t.replace(/^-+/, '')\n\t\t.replace(/-+$/, '')\n\t\t.replace(/^_+/, '')\n\t\t.replace(/_+$/, '')\n\t\t.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\n\t\t.replace(/_([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()) as ToCamelCase<S>\n\nconst IDENT = /^[A-Za-z_-][A-Za-z0-9_-]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap' | 'pick',\n\tname: Name\n): Name {\n\t// Allow your synthetic node(\"\") only for nodes (internal)\n\tif (kind === 'path' && name === '') return name.trim() as Name\n\n\tif (!name) throw new Error(`${kind} name cannot be empty`)\n\tif (!IDENT.test(name)) {\n\t\tthrow new Error(\n\t\t\t`${kind} name \"${name}\" must be a valid identifier (letters/digits/_/$, not starting with a digit).`\n\t\t)\n\t}\n\n\treturn name.trim() as Name\n}\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Path<Name, ToCamelCase<Name>, List> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamelCase(name),\n\tlist: (list ?? []) as List,\n})\n\nexport const slot = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Slot<Name, `$${ToCamelCase<Name>}`, List> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tlist: (list ?? []) as List,\n})\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\tlist?: List\n): Wrap<Name, `$${ToCamelCase<Name>}`, List, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: `$${toCamelCase(name)}`,\n\twhen,\n\tlist: (list ?? []) as List,\n})\n\nexport const pick = <\n\tconst Name extends string,\n\tconst Mode extends Record<string, readonly Segment[]>,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tmode: Mode,\n\tlist?: List\n): Pick<Name, `$${ToCamelCase<Name>}`, Mode, List> => ({\n\tkind: 'pick',\n\tname: assertValidName('pick', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tmode,\n\tlist: (list ?? []) as List,\n})\n\nexport const root = <const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs> =>\n\tbuildNode([], path('', defs)) as unknown as RoutesFromDefs<Defs>\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path\n\t\t.filter(Boolean)\n\t\t.join('/')\n\t\t.replace(/\\/{2,}/g, '/')}${search ? `?${search}` : ''}`\n\nfunction buildNode(prefix: Segment[], parent: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.list.some((node: any) => node.kind === 'keep')\n\tconst allPath = parent.kind === 'path' && parent.name ? [...prefix, parent.name] : prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = makeTarget(hasKeep(parent), allPath)\n\n\tfor (const child of parent.list) {\n\t\tif (child.kind !== 'keep') {\n\t\t\tif (child.uuid in target) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Duplicate uuid \"${String(child.uuid)}\" under \"${allPath.join('/') || '/'}\"`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (child.kind === 'slot') {\n\t\t\ttarget[child.uuid] = function bind(param: Segment) {\n\t\t\t\tconst next = [...allPath, param]\n\n\t\t\t\t// leaf slot => callable endpoint directly\n\t\t\t\tif (child.list.length === 0) {\n\t\t\t\t\treturn attachWhenAndJoin(makeTarget(true, next), next, [])\n\t\t\t\t}\n\n\t\t\t\t// non-leaf => subtree (optionally callable if keep())\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), next), buildNode(next, child))\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.list.length === 0) {\n\t\t\t\tconst leafPath = [...allPath, child.name]\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(makeTarget(true, leafPath), leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildNode(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = function wrap(arg: unknown) {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildNode(wrapped, child)\n\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), wrapped), subTree)\n\t\t\t}\n\t\t} else if (child.kind === 'pick') {\n\t\t\ttarget[child.uuid] = (value: keyof typeof child.mode) => {\n\t\t\t\tif (child.mode[value]) {\n\t\t\t\t\treturn buildNode([...allPath, ...child.mode[value]], path('', child.list))\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`pick(\"${child.name}\") got unknown value: ${String(value)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, parent.list)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], list: readonly PathDef[]) {\n\tconst when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\tconst nextPath = cond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath\n\n\t\t// If this is a callable leaf (no children), preserve callability after .when().\n\t\tif (list.length === 0 && typeof target === 'function') {\n\t\t\treturn attachWhenAndJoin(makeTarget(true, nextPath), nextPath, list)\n\t\t}\n\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildNode(nextPath, path('', list))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = function join(seg: Segment | readonly Segment[]) {\n\t\treturn when(true, seg)\n\t}\n\n\treturn target\n}\n\nconst makeTarget = (callable: boolean, currentPath: Segment[]) => {\n\treturn callable ? (search?: SParams) => url(currentPath, search) : Object.create(null)\n}\n"],"mappings":"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","names":["dialect_path_exports","__export","keep","path","pick","root","slot","wrap","__toCommonJS","toCamelCase","s","_","c","IDENT","assertValidName","kind","name","keep","path","list","slot","wrap","when","pick","mode","root","defs","buildNode","url","search","prefix","parent","hasKeep","pathDef","node","allPath","target","makeTarget","child","param","next","attachWhenAndJoin","leafPath","arg","wrapped","subTree","value","basePath","cond","seg","nextPath","callable","currentPath"]}
@@ -0,0 +1 @@
1
+ export { keep, path, pick, root, slot, wrap } from './api.cjs';
@@ -0,0 +1 @@
1
+ export { keep, path, pick, root, slot, wrap } from './api.js';
@@ -0,0 +1,2 @@
1
+ import{a as o,b as p,c as r,d as t,e,f as a}from"./chunk-TB22YO7J.js";export{o as keep,p as path,e as pick,a as root,r as slot,t as wrap};
2
+ //# sourceMappingURL=dialect-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ "use strict";var g=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var L=(t,e)=>{for(var s in e)g(t,s,{get:e[s],enumerable:!0})},P=(t,e,s,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of D(e))!C.call(t,a)&&a!==s&&g(t,a,{get:()=>e[a],enumerable:!(o=x(e,a))||o.enumerable});return t};var b=t=>P(g({},"__esModule",{value:!0}),t);var _={};L(_,{gate:()=>w,item:()=>$,make:()=>S,pick:()=>N,self:()=>k,step:()=>c});module.exports=b(_);var u=t=>t.replace(/^-+/,"").replace(/-+$/,"").replace(/^_+/,"").replace(/_+$/,"").replace(/-([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()).replace(/_([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()),A=/^[A-Za-z_-][A-Za-z0-9_-]*$/;function p(t,e){if(t==="path"&&e==="")return e.trim();if(!e)throw new Error(`${t} name cannot be empty`);if(!A.test(e))throw new Error(`${t} name "${e}" must be a valid identifier (letters/digits/_/$, not starting with a digit).`);return e.trim()}var k=()=>({kind:"keep"}),c=(t,e)=>({kind:"path",name:p("path",t),uuid:u(t),list:e??[]}),$=(t,e)=>({kind:"slot",name:p("slot",t),uuid:`$${u(t)}`,list:e??[]}),w=(t,e,s)=>({kind:"wrap",name:p("wrap",t),uuid:`$${u(t)}`,when:e,list:s??[]}),N=(t,e,s)=>({kind:"pick",name:p("pick",t),uuid:`$${u(t)}`,mode:e,list:s??[]}),S=t=>l([],c("",t)),T=(t,e)=>`/${t.filter(Boolean).join("/").replace(/\/{2,}/g,"/")}${e?`?${e}`:""}`;function l(t,e){let s=n=>n.list.some(r=>r.kind==="keep"),o=e.kind==="path"&&e.name?[...t,e.name]:t,a=i(s(e),o);for(let n of e.list){if(n.kind!=="keep"&&n.uuid in a)throw new Error(`Duplicate uuid "${String(n.uuid)}" under "${o.join("/")||"/"}"`);if(n.kind==="slot")a[n.uuid]=function(f){let d=[...o,f];return n.list.length===0?m(i(!0,d),d,[]):Object.assign(i(s(n),d),l(d,n))};else if(n.kind==="path")if(n.list.length===0){let r=[...o,n.name];a[n.uuid]=m(i(!0,r),r,[])}else a[n.uuid]=l(o,n);else n.kind==="wrap"?a[n.uuid]=function(f){let h=n.when(f)?[...o,n.name]:o,y=l(h,n);return Object.assign(i(s(n),h),y)}:n.kind==="pick"&&(a[n.uuid]=r=>{if(n.mode[r])return l([...o,...n.mode[r]],c("",n.list));throw new Error(`pick("${n.name}") got unknown value: ${String(r)}`)})}return m(a,o,e.list)}function m(t,e,s){let o=(a,n)=>{let r=a?[...e,...Array.isArray(n)?n:[n]]:e;return s.length===0&&typeof t=="function"?m(i(!0,r),r,s):l(r,c("",s))};return t.$when=o,t.$join=function(n){return o(!0,n)},t}var i=(t,e)=>t?s=>T(e,s):Object.create(null);0&&(module.exports={gate,item,make,pick,self,step});
2
+ //# sourceMappingURL=dialect-step.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dialect-step.ts","../src/api.ts"],"sourcesContent":["export { root as make, path as step, slot as item, keep as self, wrap as gate, pick } from './api'\n","import {\n\tKeep,\n\tPath,\n\tPathDef,\n\tPick,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tToCamelCase,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamelCase = <S extends string>(s: S) =>\n\ts\n\t\t.replace(/^-+/, '')\n\t\t.replace(/-+$/, '')\n\t\t.replace(/^_+/, '')\n\t\t.replace(/_+$/, '')\n\t\t.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\n\t\t.replace(/_([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()) as ToCamelCase<S>\n\nconst IDENT = /^[A-Za-z_-][A-Za-z0-9_-]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap' | 'pick',\n\tname: Name\n): Name {\n\t// Allow your synthetic node(\"\") only for nodes (internal)\n\tif (kind === 'path' && name === '') return name.trim() as Name\n\n\tif (!name) throw new Error(`${kind} name cannot be empty`)\n\tif (!IDENT.test(name)) {\n\t\tthrow new Error(\n\t\t\t`${kind} name \"${name}\" must be a valid identifier (letters/digits/_/$, not starting with a digit).`\n\t\t)\n\t}\n\n\treturn name.trim() as Name\n}\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Path<Name, ToCamelCase<Name>, List> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamelCase(name),\n\tlist: (list ?? []) as List,\n})\n\nexport const slot = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Slot<Name, `$${ToCamelCase<Name>}`, List> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tlist: (list ?? []) as List,\n})\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\tlist?: List\n): Wrap<Name, `$${ToCamelCase<Name>}`, List, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: `$${toCamelCase(name)}`,\n\twhen,\n\tlist: (list ?? []) as List,\n})\n\nexport const pick = <\n\tconst Name extends string,\n\tconst Mode extends Record<string, readonly Segment[]>,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tmode: Mode,\n\tlist?: List\n): Pick<Name, `$${ToCamelCase<Name>}`, Mode, List> => ({\n\tkind: 'pick',\n\tname: assertValidName('pick', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tmode,\n\tlist: (list ?? []) as List,\n})\n\nexport const root = <const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs> =>\n\tbuildNode([], path('', defs)) as unknown as RoutesFromDefs<Defs>\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path\n\t\t.filter(Boolean)\n\t\t.join('/')\n\t\t.replace(/\\/{2,}/g, '/')}${search ? `?${search}` : ''}`\n\nfunction buildNode(prefix: Segment[], parent: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.list.some((node: any) => node.kind === 'keep')\n\tconst allPath = parent.kind === 'path' && parent.name ? [...prefix, parent.name] : prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = makeTarget(hasKeep(parent), allPath)\n\n\tfor (const child of parent.list) {\n\t\tif (child.kind !== 'keep') {\n\t\t\tif (child.uuid in target) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Duplicate uuid \"${String(child.uuid)}\" under \"${allPath.join('/') || '/'}\"`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (child.kind === 'slot') {\n\t\t\ttarget[child.uuid] = function bind(param: Segment) {\n\t\t\t\tconst next = [...allPath, param]\n\n\t\t\t\t// leaf slot => callable endpoint directly\n\t\t\t\tif (child.list.length === 0) {\n\t\t\t\t\treturn attachWhenAndJoin(makeTarget(true, next), next, [])\n\t\t\t\t}\n\n\t\t\t\t// non-leaf => subtree (optionally callable if keep())\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), next), buildNode(next, child))\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.list.length === 0) {\n\t\t\t\tconst leafPath = [...allPath, child.name]\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(makeTarget(true, leafPath), leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildNode(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = function wrap(arg: unknown) {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildNode(wrapped, child)\n\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), wrapped), subTree)\n\t\t\t}\n\t\t} else if (child.kind === 'pick') {\n\t\t\ttarget[child.uuid] = (value: keyof typeof child.mode) => {\n\t\t\t\tif (child.mode[value]) {\n\t\t\t\t\treturn buildNode([...allPath, ...child.mode[value]], path('', child.list))\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`pick(\"${child.name}\") got unknown value: ${String(value)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, parent.list)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], list: readonly PathDef[]) {\n\tconst when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\tconst nextPath = cond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath\n\n\t\t// If this is a callable leaf (no children), preserve callability after .when().\n\t\tif (list.length === 0 && typeof target === 'function') {\n\t\t\treturn attachWhenAndJoin(makeTarget(true, nextPath), nextPath, list)\n\t\t}\n\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildNode(nextPath, path('', list))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = function join(seg: Segment | readonly Segment[]) {\n\t\treturn when(true, seg)\n\t}\n\n\treturn target\n}\n\nconst makeTarget = (callable: boolean, currentPath: Segment[]) => {\n\treturn callable ? (search?: SParams) => url(currentPath, search) : Object.create(null)\n}\n"],"mappings":"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","names":["dialect_step_exports","__export","wrap","slot","root","pick","keep","path","__toCommonJS","toCamelCase","s","_","c","IDENT","assertValidName","kind","name","keep","path","list","slot","wrap","when","pick","mode","root","defs","buildNode","url","search","prefix","parent","hasKeep","pathDef","node","allPath","target","makeTarget","child","param","next","attachWhenAndJoin","leafPath","arg","wrapped","subTree","value","basePath","cond","seg","nextPath","callable","currentPath"]}
@@ -0,0 +1 @@
1
+ export { wrap as gate, slot as item, root as make, pick, keep as self, path as step } from './api.cjs';
@@ -0,0 +1 @@
1
+ export { wrap as gate, slot as item, root as make, pick, keep as self, path as step } from './api.js';
@@ -0,0 +1,2 @@
1
+ import{a,b as e,c as s,d as t,e as p,f as o}from"./chunk-TB22YO7J.js";export{t as gate,s as item,o as make,p as pick,a as self,e as step};
2
+ //# sourceMappingURL=dialect-step.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ "use strict";var g=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var L=(t,e)=>{for(var s in e)g(t,s,{get:e[s],enumerable:!0})},P=(t,e,s,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of D(e))!C.call(t,o)&&o!==s&&g(t,o,{get:()=>e[o],enumerable:!(a=x(e,o))||a.enumerable});return t};var b=t=>P(g({},"__esModule",{value:!0}),t);var _={};L(_,{grow:()=>S,nest:()=>w,pick:()=>N,seed:()=>$,tree:()=>c,twig:()=>k});module.exports=b(_);var u=t=>t.replace(/^-+/,"").replace(/-+$/,"").replace(/^_+/,"").replace(/_+$/,"").replace(/-([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()).replace(/_([a-zA-Z0-9])/g,(e,s)=>s.toUpperCase()),A=/^[A-Za-z_-][A-Za-z0-9_-]*$/;function p(t,e){if(t==="path"&&e==="")return e.trim();if(!e)throw new Error(`${t} name cannot be empty`);if(!A.test(e))throw new Error(`${t} name "${e}" must be a valid identifier (letters/digits/_/$, not starting with a digit).`);return e.trim()}var k=()=>({kind:"keep"}),c=(t,e)=>({kind:"path",name:p("path",t),uuid:u(t),list:e??[]}),$=(t,e)=>({kind:"slot",name:p("slot",t),uuid:`$${u(t)}`,list:e??[]}),w=(t,e,s)=>({kind:"wrap",name:p("wrap",t),uuid:`$${u(t)}`,when:e,list:s??[]}),N=(t,e,s)=>({kind:"pick",name:p("pick",t),uuid:`$${u(t)}`,mode:e,list:s??[]}),S=t=>l([],c("",t)),T=(t,e)=>`/${t.filter(Boolean).join("/").replace(/\/{2,}/g,"/")}${e?`?${e}`:""}`;function l(t,e){let s=n=>n.list.some(r=>r.kind==="keep"),a=e.kind==="path"&&e.name?[...t,e.name]:t,o=i(s(e),a);for(let n of e.list){if(n.kind!=="keep"&&n.uuid in o)throw new Error(`Duplicate uuid "${String(n.uuid)}" under "${a.join("/")||"/"}"`);if(n.kind==="slot")o[n.uuid]=function(f){let d=[...a,f];return n.list.length===0?m(i(!0,d),d,[]):Object.assign(i(s(n),d),l(d,n))};else if(n.kind==="path")if(n.list.length===0){let r=[...a,n.name];o[n.uuid]=m(i(!0,r),r,[])}else o[n.uuid]=l(a,n);else n.kind==="wrap"?o[n.uuid]=function(f){let h=n.when(f)?[...a,n.name]:a,y=l(h,n);return Object.assign(i(s(n),h),y)}:n.kind==="pick"&&(o[n.uuid]=r=>{if(n.mode[r])return l([...a,...n.mode[r]],c("",n.list));throw new Error(`pick("${n.name}") got unknown value: ${String(r)}`)})}return m(o,a,e.list)}function m(t,e,s){let a=(o,n)=>{let r=o?[...e,...Array.isArray(n)?n:[n]]:e;return s.length===0&&typeof t=="function"?m(i(!0,r),r,s):l(r,c("",s))};return t.$when=a,t.$join=function(n){return a(!0,n)},t}var i=(t,e)=>t?s=>T(e,s):Object.create(null);0&&(module.exports={grow,nest,pick,seed,tree,twig});
2
+ //# sourceMappingURL=dialect-tree.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dialect-tree.ts","../src/api.ts"],"sourcesContent":["export { root as grow, path as tree, slot as seed, keep as twig, wrap as nest, pick } from './api'\n","import {\n\tKeep,\n\tPath,\n\tPathDef,\n\tPick,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tToCamelCase,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamelCase = <S extends string>(s: S) =>\n\ts\n\t\t.replace(/^-+/, '')\n\t\t.replace(/-+$/, '')\n\t\t.replace(/^_+/, '')\n\t\t.replace(/_+$/, '')\n\t\t.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\n\t\t.replace(/_([a-zA-Z0-9])/g, (_, c) => c.toUpperCase()) as ToCamelCase<S>\n\nconst IDENT = /^[A-Za-z_-][A-Za-z0-9_-]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap' | 'pick',\n\tname: Name\n): Name {\n\t// Allow your synthetic node(\"\") only for nodes (internal)\n\tif (kind === 'path' && name === '') return name.trim() as Name\n\n\tif (!name) throw new Error(`${kind} name cannot be empty`)\n\tif (!IDENT.test(name)) {\n\t\tthrow new Error(\n\t\t\t`${kind} name \"${name}\" must be a valid identifier (letters/digits/_/$, not starting with a digit).`\n\t\t)\n\t}\n\n\treturn name.trim() as Name\n}\n\n// ---------- DSL helpers (typed) ----------\nexport const keep = (): Keep => ({ kind: 'keep' })\n\nexport const path = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Path<Name, ToCamelCase<Name>, List> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamelCase(name),\n\tlist: (list ?? []) as List,\n})\n\nexport const slot = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tlist?: List\n): Slot<Name, `$${ToCamelCase<Name>}`, List> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tlist: (list ?? []) as List,\n})\n\nexport const wrap = <\n\tconst Name extends string,\n\tconst List extends readonly PathDef[] = readonly [],\n\tArgs = unknown,\n>(\n\tname: Name,\n\twhen: (args: Args) => boolean,\n\tlist?: List\n): Wrap<Name, `$${ToCamelCase<Name>}`, List, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: `$${toCamelCase(name)}`,\n\twhen,\n\tlist: (list ?? []) as List,\n})\n\nexport const pick = <\n\tconst Name extends string,\n\tconst Mode extends Record<string, readonly Segment[]>,\n\tconst List extends readonly PathDef[] = readonly [],\n>(\n\tname: Name,\n\tmode: Mode,\n\tlist?: List\n): Pick<Name, `$${ToCamelCase<Name>}`, Mode, List> => ({\n\tkind: 'pick',\n\tname: assertValidName('pick', name),\n\tuuid: `$${toCamelCase(name)}`,\n\tmode,\n\tlist: (list ?? []) as List,\n})\n\nexport const root = <const Defs extends readonly PathDef[]>(defs: Defs): RoutesFromDefs<Defs> =>\n\tbuildNode([], path('', defs)) as unknown as RoutesFromDefs<Defs>\n\n// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path\n\t\t.filter(Boolean)\n\t\t.join('/')\n\t\t.replace(/\\/{2,}/g, '/')}${search ? `?${search}` : ''}`\n\nfunction buildNode(prefix: Segment[], parent: SlotDef) {\n\tconst hasKeep = (pathDef: SlotDef) => pathDef.list.some((node: any) => node.kind === 'keep')\n\tconst allPath = parent.kind === 'path' && parent.name ? [...prefix, parent.name] : prefix\n\n\t// If there is a keep(), the path itself is callable and acts as \"keep\"\n\tconst target: any = makeTarget(hasKeep(parent), allPath)\n\n\tfor (const child of parent.list) {\n\t\tif (child.kind !== 'keep') {\n\t\t\tif (child.uuid in target) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Duplicate uuid \"${String(child.uuid)}\" under \"${allPath.join('/') || '/'}\"`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (child.kind === 'slot') {\n\t\t\ttarget[child.uuid] = function bind(param: Segment) {\n\t\t\t\tconst next = [...allPath, param]\n\n\t\t\t\t// leaf slot => callable endpoint directly\n\t\t\t\tif (child.list.length === 0) {\n\t\t\t\t\treturn attachWhenAndJoin(makeTarget(true, next), next, [])\n\t\t\t\t}\n\n\t\t\t\t// non-leaf => subtree (optionally callable if keep())\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), next), buildNode(next, child))\n\t\t\t}\n\t\t} else if (child.kind === 'path') {\n\t\t\tif (child.list.length === 0) {\n\t\t\t\tconst leafPath = [...allPath, child.name]\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(makeTarget(true, leafPath), leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildNode(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = function wrap(arg: unknown) {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.name] : allPath\n\t\t\t\tconst subTree = buildNode(wrapped, child)\n\n\t\t\t\treturn Object.assign(makeTarget(hasKeep(child), wrapped), subTree)\n\t\t\t}\n\t\t} else if (child.kind === 'pick') {\n\t\t\ttarget[child.uuid] = (value: keyof typeof child.mode) => {\n\t\t\t\tif (child.mode[value]) {\n\t\t\t\t\treturn buildNode([...allPath, ...child.mode[value]], path('', child.list))\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`pick(\"${child.name}\") got unknown value: ${String(value)}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attachWhenAndJoin(target, allPath, parent.list)\n}\n\nfunction attachWhenAndJoin(target: any, basePath: Segment[], list: readonly PathDef[]) {\n\tconst when = (cond: boolean, seg: Segment | readonly Segment[]) => {\n\t\tconst nextPath = cond ? [...basePath, ...(Array.isArray(seg) ? seg : [seg])] : basePath\n\n\t\t// If this is a callable leaf (no children), preserve callability after .when().\n\t\tif (list.length === 0 && typeof target === 'function') {\n\t\t\treturn attachWhenAndJoin(makeTarget(true, nextPath), nextPath, list)\n\t\t}\n\n\t\t// Rebuild \"same subtree\" at a new prefix:\n\t\t// Use a synthetic path '' so we don't append an extra segment name.\n\t\treturn buildNode(nextPath, path('', list))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = function join(seg: Segment | readonly Segment[]) {\n\t\treturn when(true, seg)\n\t}\n\n\treturn target\n}\n\nconst makeTarget = (callable: boolean, currentPath: Segment[]) => {\n\treturn callable ? (search?: SParams) => url(currentPath, search) : Object.create(null)\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,UAAAE,EAAA,SAAAC,EAAA,SAAAC,EAAA,SAAAC,EAAA,SAAAC,EAAA,SAAAC,IAAA,eAAAC,EAAAR,GCeA,IAAMS,EAAiCC,GACtCA,EACE,QAAQ,MAAO,EAAE,EACjB,QAAQ,MAAO,EAAE,EACjB,QAAQ,MAAO,EAAE,EACjB,QAAQ,MAAO,EAAE,EACjB,QAAQ,kBAAmB,CAACC,EAAGC,IAAMA,EAAE,YAAY,CAAC,EACpD,QAAQ,kBAAmB,CAACD,EAAGC,IAAMA,EAAE,YAAY,CAAC,EAEjDC,EAAQ,6BAEd,SAASC,EACRC,EACAC,EACO,CAEP,GAAID,IAAS,QAAUC,IAAS,GAAI,OAAOA,EAAK,KAAK,EAErD,GAAI,CAACA,EAAM,MAAM,IAAI,MAAM,GAAGD,CAAI,uBAAuB,EACzD,GAAI,CAACF,EAAM,KAAKG,CAAI,EACnB,MAAM,IAAI,MACT,GAAGD,CAAI,UAAUC,CAAI,+EACtB,EAGD,OAAOA,EAAK,KAAK,CAClB,CAGO,IAAMC,EAAO,KAAa,CAAE,KAAM,MAAO,GAEnCC,EAAO,CAInBF,EACAG,KAC0C,CAC1C,KAAM,OACN,KAAML,EAAgB,OAAQE,CAAI,EAClC,KAAMP,EAAYO,CAAI,EACtB,KAAOG,GAAQ,CAAC,CACjB,GAEaC,EAAO,CAInBJ,EACAG,KACgD,CAChD,KAAM,OACN,KAAML,EAAgB,OAAQE,CAAI,EAClC,KAAM,IAAIP,EAAYO,CAAI,CAAC,GAC3B,KAAOG,GAAQ,CAAC,CACjB,GAEaE,EAAO,CAKnBL,EACAM,EACAH,KACsD,CACtD,KAAM,OACN,KAAML,EAAgB,OAAQE,CAAI,EAClC,KAAM,IAAIP,EAAYO,CAAI,CAAC,GAC3B,KAAAM,EACA,KAAOH,GAAQ,CAAC,CACjB,GAEaI,EAAO,CAKnBP,EACAQ,EACAL,KACsD,CACtD,KAAM,OACN,KAAML,EAAgB,OAAQE,CAAI,EAClC,KAAM,IAAIP,EAAYO,CAAI,CAAC,GAC3B,KAAAQ,EACA,KAAOL,GAAQ,CAAC,CACjB,GAEaM,EAA+CC,GAC3DC,EAAU,CAAC,EAAGT,EAAK,GAAIQ,CAAI,CAAC,EAGvBE,EAAM,CAACV,EAAiBW,IAC7B,IAAIX,EACF,OAAO,OAAO,EACd,KAAK,GAAG,EACR,QAAQ,UAAW,GAAG,CAAC,GAAGW,EAAS,IAAIA,CAAM,GAAK,EAAE,GAEvD,SAASF,EAAUG,EAAmBC,EAAiB,CACtD,IAAMC,EAAWC,GAAqBA,EAAQ,KAAK,KAAMC,GAAcA,EAAK,OAAS,MAAM,EACrFC,EAAUJ,EAAO,OAAS,QAAUA,EAAO,KAAO,CAAC,GAAGD,EAAQC,EAAO,IAAI,EAAID,EAG7EM,EAAcC,EAAWL,EAAQD,CAAM,EAAGI,CAAO,EAEvD,QAAWG,KAASP,EAAO,KAAM,CAChC,GAAIO,EAAM,OAAS,QACdA,EAAM,QAAQF,EACjB,MAAM,IAAI,MACT,mBAAmB,OAAOE,EAAM,IAAI,CAAC,YAAYH,EAAQ,KAAK,GAAG,GAAK,GAAG,GAC1E,EAIF,GAAIG,EAAM,OAAS,OAClBF,EAAOE,EAAM,IAAI,EAAI,SAAcC,EAAgB,CAClD,IAAMC,EAAO,CAAC,GAAGL,EAASI,CAAK,EAG/B,OAAID,EAAM,KAAK,SAAW,EAClBG,EAAkBJ,EAAW,GAAMG,CAAI,EAAGA,EAAM,CAAC,CAAC,EAInD,OAAO,OAAOH,EAAWL,EAAQM,CAAK,EAAGE,CAAI,EAAGb,EAAUa,EAAMF,CAAK,CAAC,CAC9E,UACUA,EAAM,OAAS,OACzB,GAAIA,EAAM,KAAK,SAAW,EAAG,CAC5B,IAAMI,EAAW,CAAC,GAAGP,EAASG,EAAM,IAAI,EACxCF,EAAOE,EAAM,IAAI,EAAIG,EAAkBJ,EAAW,GAAMK,CAAQ,EAAGA,EAAU,CAAC,CAAC,CAChF,MACCN,EAAOE,EAAM,IAAI,EAAIX,EAAUQ,EAASG,CAAK,OAEpCA,EAAM,OAAS,OACzBF,EAAOE,EAAM,IAAI,EAAI,SAAcK,EAAc,CAEhD,IAAMC,EADUN,EAAM,KAAKK,CAAG,EACJ,CAAC,GAAGR,EAASG,EAAM,IAAI,EAAIH,EAC/CU,EAAUlB,EAAUiB,EAASN,CAAK,EAExC,OAAO,OAAO,OAAOD,EAAWL,EAAQM,CAAK,EAAGM,CAAO,EAAGC,CAAO,CAClE,EACUP,EAAM,OAAS,SACzBF,EAAOE,EAAM,IAAI,EAAKQ,GAAmC,CACxD,GAAIR,EAAM,KAAKQ,CAAK,EACnB,OAAOnB,EAAU,CAAC,GAAGQ,EAAS,GAAGG,EAAM,KAAKQ,CAAK,CAAC,EAAG5B,EAAK,GAAIoB,EAAM,IAAI,CAAC,EAEzE,MAAM,IAAI,MAAM,SAASA,EAAM,IAAI,yBAAyB,OAAOQ,CAAK,CAAC,EAAE,CAE7E,EAEF,CAEA,OAAOL,EAAkBL,EAAQD,EAASJ,EAAO,IAAI,CACtD,CAEA,SAASU,EAAkBL,EAAaW,EAAqB5B,EAA0B,CACtF,IAAMG,EAAO,CAAC0B,EAAeC,IAAsC,CAClE,IAAMC,EAAWF,EAAO,CAAC,GAAGD,EAAU,GAAI,MAAM,QAAQE,CAAG,EAAIA,EAAM,CAACA,CAAG,CAAE,EAAIF,EAG/E,OAAI5B,EAAK,SAAW,GAAK,OAAOiB,GAAW,WACnCK,EAAkBJ,EAAW,GAAMa,CAAQ,EAAGA,EAAU/B,CAAI,EAK7DQ,EAAUuB,EAAUhC,EAAK,GAAIC,CAAI,CAAC,CAC1C,EAEA,OAAAiB,EAAO,MAAQd,EACfc,EAAO,MAAQ,SAAca,EAAmC,CAC/D,OAAO3B,EAAK,GAAM2B,CAAG,CACtB,EAEOb,CACR,CAEA,IAAMC,EAAa,CAACc,EAAmBC,IAC/BD,EAAYtB,GAAqBD,EAAIwB,EAAavB,CAAM,EAAI,OAAO,OAAO,IAAI","names":["dialect_tree_exports","__export","root","wrap","pick","slot","path","keep","__toCommonJS","toCamelCase","s","_","c","IDENT","assertValidName","kind","name","keep","path","list","slot","wrap","when","pick","mode","root","defs","buildNode","url","search","prefix","parent","hasKeep","pathDef","node","allPath","target","makeTarget","child","param","next","attachWhenAndJoin","leafPath","arg","wrapped","subTree","value","basePath","cond","seg","nextPath","callable","currentPath"]}
@@ -0,0 +1 @@
1
+ export { root as grow, wrap as nest, pick, slot as seed, path as tree, keep as twig } from './api.cjs';
@@ -0,0 +1 @@
1
+ export { root as grow, wrap as nest, pick, slot as seed, path as tree, keep as twig } from './api.js';
@@ -0,0 +1,2 @@
1
+ import{a as e,b as s,c as a,d as t,e as o,f as r}from"./chunk-TB22YO7J.js";export{r as grow,t as nest,o as pick,a as seed,s as tree,e as twig};
2
+ //# sourceMappingURL=dialect-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "route-sprout",
3
- "version": "1.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "A tiny, cute DSL that grows type-safe, composable URL builders from a declarative route tree.",
5
5
  "author": "Piotr Siatkowski <p.siatkowski@gmail.com>",
6
6
  "license": "MIT",
@@ -24,9 +24,9 @@
24
24
  },
25
25
  "sideEffects": false,
26
26
  "type": "module",
27
- "main": "./dist/index.cjs",
28
- "module": "./dist/index.js",
29
- "types": "./dist/index.d.ts",
27
+ "main": "./dist/api.cjs",
28
+ "module": "./dist/api.js",
29
+ "types": "./dist/api.d.ts",
30
30
  "files": [
31
31
  "dist/**/*"
32
32
  ],
@@ -63,7 +63,7 @@
63
63
  }
64
64
  },
65
65
  "scripts": {
66
- "build": "vitest && tsup src/api.ts --format esm,cjs --dts --clean --sourcemap --minify",
66
+ "build": "vitest && tsup src/api.ts src/dialect-node.ts src/dialect-path.ts src/dialect-step.ts src/dialect-tree.ts --format esm,cjs --dts --clean --sourcemap --minify",
67
67
  "publish": "npm publish --access public",
68
68
  "test": "vitest"
69
69
  },