route-sprout 2.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 +36 -18
- package/dist/api.cjs +1 -1
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +49 -55
- package/dist/api.d.ts +49 -55
- package/dist/api.js +1 -1
- package/dist/chunk-TB22YO7J.js +2 -0
- package/dist/chunk-TB22YO7J.js.map +1 -0
- package/dist/dialect-node.cjs +1 -1
- package/dist/dialect-node.cjs.map +1 -1
- package/dist/dialect-node.d.cts +1 -1
- package/dist/dialect-node.d.ts +1 -1
- package/dist/dialect-node.js +1 -1
- package/dist/dialect-path.cjs +1 -1
- package/dist/dialect-path.cjs.map +1 -1
- package/dist/dialect-path.d.cts +1 -1
- package/dist/dialect-path.d.ts +1 -1
- package/dist/dialect-path.js +1 -1
- package/dist/dialect-step.cjs +1 -1
- package/dist/dialect-step.cjs.map +1 -1
- package/dist/dialect-step.d.cts +1 -1
- package/dist/dialect-step.d.ts +1 -1
- package/dist/dialect-step.js +1 -1
- package/dist/dialect-tree.cjs +1 -1
- package/dist/dialect-tree.cjs.map +1 -1
- package/dist/dialect-tree.d.cts +1 -1
- package/dist/dialect-tree.d.ts +1 -1
- package/dist/dialect-tree.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-5ZW66SPO.js +0 -2
- package/dist/chunk-5ZW66SPO.js.map +0 -1
package/README.md
CHANGED
|
@@ -41,8 +41,8 @@ const Api = root([
|
|
|
41
41
|
|
|
42
42
|
Api.invoices(); // "/invoices"
|
|
43
43
|
Api.invoices("page=1"); // "/invoices?page=1"
|
|
44
|
-
Api.invoices
|
|
45
|
-
Api.invoices
|
|
44
|
+
Api.invoices.$id("abc")("a=1"); // "/invoices/abc?a=1"
|
|
45
|
+
Api.invoices.$id("abc").customers(); // "/invoices/abc/customers"
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
---
|
|
@@ -69,7 +69,7 @@ When you have lots of endpoints, you usually end up with:
|
|
|
69
69
|
This DSL gives you:
|
|
70
70
|
|
|
71
71
|
- a single, declarative source of truth
|
|
72
|
-
- fluent, discoverable usage (`Api.invoices
|
|
72
|
+
- fluent, discoverable usage (`Api.invoices.$id("x").customers()`)
|
|
73
73
|
- TypeScript autocomplete and type checking from **usage**, not comments
|
|
74
74
|
|
|
75
75
|
---
|
|
@@ -111,7 +111,7 @@ The `name` is only the **property key**. It is **not** inserted into the path.
|
|
|
111
111
|
|
|
112
112
|
```ts
|
|
113
113
|
path("invoices", [slot("id")]);
|
|
114
|
-
// Api.invoices
|
|
114
|
+
// Api.invoices.$id("abc")() -> "/invoices/abc"
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
With children:
|
|
@@ -124,7 +124,7 @@ path("invoices", [
|
|
|
124
124
|
]),
|
|
125
125
|
]);
|
|
126
126
|
|
|
127
|
-
// Api.invoices
|
|
127
|
+
// Api.invoices.$id("abc").customers() -> "/invoices/abc/customers"
|
|
128
128
|
```
|
|
129
129
|
|
|
130
130
|
### `wrap(name, predicate, children?)`
|
|
@@ -143,12 +143,29 @@ path("core", [
|
|
|
143
143
|
]),
|
|
144
144
|
]);
|
|
145
145
|
|
|
146
|
-
Api.core
|
|
147
|
-
Api.core
|
|
146
|
+
Api.core.$admin({ isAdmin: true }).invoices(); // "/core/admin/invoices"
|
|
147
|
+
Api.core.$admin({ isAdmin: false }).invoices(); // "/core/invoices"
|
|
148
148
|
```
|
|
149
149
|
|
|
150
150
|
> `wrap` is ideal for *well-known*, reusable gates: `admin`, `v2`, `tenant`, etc.
|
|
151
151
|
|
|
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
|
+
|
|
152
169
|
### `.$when(cond, segment | segment[])`
|
|
153
170
|
|
|
154
171
|
Ad‑hoc conditional segment insertion at **runtime**, anywhere in the chain.
|
|
@@ -156,7 +173,7 @@ Ad‑hoc conditional segment insertion at **runtime**, anywhere in the chain.
|
|
|
156
173
|
```ts
|
|
157
174
|
Api.core.$when(isAdmin, "admin").invoices();
|
|
158
175
|
Api.core.$when(true, ["tenant", tenantId]).invoices();
|
|
159
|
-
Api.invoices
|
|
176
|
+
Api.invoices.$id("abc").$when(flags.preview, "preview").activities();
|
|
160
177
|
```
|
|
161
178
|
|
|
162
179
|
- `cond = false` → no-op
|
|
@@ -216,18 +233,18 @@ export const Api = root([
|
|
|
216
233
|
|
|
217
234
|
// usage
|
|
218
235
|
Api.invoices(); // "/invoices"
|
|
219
|
-
Api.invoices
|
|
236
|
+
Api.invoices.$id("123").customers(); // "/invoices/123/customers"
|
|
220
237
|
|
|
221
238
|
// runtime insert
|
|
222
239
|
Api.core.$when(true, "v2").invoices(); // "/core/v2/invoices"
|
|
223
|
-
Api.core
|
|
240
|
+
Api.core.$admin({ isAdmin: true }).$when(true, "v2").invoices(); // "/core/admin/v2/invoices"
|
|
224
241
|
```
|
|
225
242
|
|
|
226
243
|
### Autocomplete-friendly patterns
|
|
227
244
|
Because everything is computed from the definition tree, your editor can autocomplete:
|
|
228
245
|
|
|
229
246
|
- paths (`Api.invoices`, `Api.orders.export`)
|
|
230
|
-
- slots (`Api.invoices
|
|
247
|
+
- slots (`Api.invoices.$id(…)`)
|
|
231
248
|
- nested children (`…id("x").customers()`)
|
|
232
249
|
|
|
233
250
|
---
|
|
@@ -237,9 +254,10 @@ Because everything is computed from the definition tree, your editor can autocom
|
|
|
237
254
|
### Exports
|
|
238
255
|
|
|
239
256
|
- `root(defs)`
|
|
240
|
-
- `path(name,
|
|
241
|
-
- `slot(name,
|
|
242
|
-
- `wrap(name, when,
|
|
257
|
+
- `path(name, defs?)`
|
|
258
|
+
- `slot(name, defs?)`
|
|
259
|
+
- `wrap(name, when, defs?)`
|
|
260
|
+
- `pick(name, mode, defs?)`
|
|
243
261
|
- `keep()`
|
|
244
262
|
|
|
245
263
|
### Path level builders
|
|
@@ -272,7 +290,7 @@ const Api = root([
|
|
|
272
290
|
path("invoices", [keep(), slot("id")]),
|
|
273
291
|
]);
|
|
274
292
|
|
|
275
|
-
Api.invoices
|
|
293
|
+
Api.invoices.$id("123")(); // "/invoices/123"
|
|
276
294
|
```
|
|
277
295
|
|
|
278
296
|
#### `route-sprout/dialect-step`
|
|
@@ -302,7 +320,7 @@ const Api = grow([
|
|
|
302
320
|
]),
|
|
303
321
|
]);
|
|
304
322
|
|
|
305
|
-
Api.core
|
|
323
|
+
Api.core.$admin({ isAdmin: true }).jobs(); // "/core/admin/jobs"
|
|
306
324
|
```
|
|
307
325
|
|
|
308
326
|
#### `route-sprout/dialect-node`
|
|
@@ -315,7 +333,7 @@ const Api = link([
|
|
|
315
333
|
node("tasks", [base(), bind("id", [node("logs")])]),
|
|
316
334
|
]);
|
|
317
335
|
|
|
318
|
-
Api.tasks
|
|
336
|
+
Api.tasks.$id("x").logs(); // "/tasks/x/logs"
|
|
319
337
|
```
|
|
320
338
|
|
|
321
339
|
### Mix-and-match?
|
|
@@ -347,7 +365,7 @@ import { root, path, slot, keep } from "route-sprout";
|
|
|
347
365
|
test("builds routes", () => {
|
|
348
366
|
const Api = root([path("invoices", [keep(), slot("id")])] as const);
|
|
349
367
|
expect(Api.invoices()).toBe("/invoices");
|
|
350
|
-
expect(Api.invoices
|
|
368
|
+
expect(Api.invoices.$id("x")()).toBe("/invoices/x");
|
|
351
369
|
});
|
|
352
370
|
```
|
|
353
371
|
|
package/dist/api.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
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 {\n\
|
|
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,6 +1,9 @@
|
|
|
1
1
|
// ---------- Helpers ----------------------
|
|
2
|
-
type
|
|
3
|
-
|
|
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>>}`
|
|
4
7
|
: S
|
|
5
8
|
|
|
6
9
|
// ---------- Shared public types ----------
|
|
@@ -13,93 +16,84 @@ type Keep = { kind: 'keep' }
|
|
|
13
16
|
type Path<
|
|
14
17
|
Name extends string = string,
|
|
15
18
|
Uuid extends string = string,
|
|
16
|
-
|
|
17
|
-
> = { kind: 'path'; name: Name; uuid: Uuid;
|
|
19
|
+
List extends readonly PathDef[] = readonly PathDef[],
|
|
20
|
+
> = { kind: 'path'; name: Name; uuid: Uuid; list: List }
|
|
18
21
|
|
|
19
22
|
type Slot<
|
|
20
23
|
Name extends string = string,
|
|
21
24
|
Uuid extends string = string,
|
|
22
|
-
|
|
23
|
-
> = { kind: 'slot'; name: Name; uuid: Uuid;
|
|
25
|
+
List extends readonly PathDef[] = readonly PathDef[],
|
|
26
|
+
> = { kind: 'slot'; name: Name; uuid: Uuid; list: List }
|
|
24
27
|
|
|
25
28
|
type Wrap<
|
|
26
29
|
Name extends string = string,
|
|
27
30
|
Uuid extends string = string,
|
|
28
|
-
|
|
31
|
+
List extends readonly PathDef[] = readonly PathDef[],
|
|
29
32
|
Args = unknown,
|
|
30
|
-
> = { kind: 'wrap'; name: Name; uuid: Uuid;
|
|
33
|
+
> = { kind: 'wrap'; name: Name; uuid: Uuid; list: List; when: (args: Args) => boolean }
|
|
31
34
|
|
|
32
|
-
type
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
37
42
|
|
|
38
43
|
// ---------- Type-level route builder ----------
|
|
39
|
-
|
|
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 & {
|
|
40
49
|
$when(cond: boolean, seg: Segment | readonly Segment[]): this
|
|
41
50
|
$join(seg: Segment | readonly Segment[]): this
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
type
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
type NonKeepChildren<Rest extends readonly PathDef[]> = Exclude<Rest[number], Keep>
|
|
48
|
-
|
|
49
|
-
type PropsFromChildren<Rest extends readonly PathDef[]> = {
|
|
50
|
-
[C in NonKeepChildren<Rest> as C extends { name: infer N extends string }
|
|
51
|
-
? N
|
|
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>
|
|
52
56
|
: never]: C extends Path<any, any, any>
|
|
53
57
|
? RouteFromPath<C>
|
|
54
58
|
: C extends Slot<any, any, any>
|
|
55
59
|
? RouteFromSlot<C>
|
|
56
60
|
: C extends Wrap<any, any, any, any>
|
|
57
61
|
? RouteFromWrap<C>
|
|
58
|
-
:
|
|
62
|
+
: C extends Pick<any, any, any, any>
|
|
63
|
+
? RouteFromPick<C>
|
|
64
|
+
: never
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
type
|
|
62
|
-
|
|
63
|
-
// Example: apply it to the outputs
|
|
64
|
-
type RouteFromPath<N extends Path<any, any, any>> = WithWhen<
|
|
65
|
-
N['rest'] extends readonly []
|
|
67
|
+
type RouteFromPath<Node extends Path<any, any, any>> = WithWhen<
|
|
68
|
+
Node['list'] extends readonly []
|
|
66
69
|
? (search?: SParams) => string
|
|
67
|
-
:
|
|
68
|
-
? ((search?: SParams) => string) & PropsFromChildren<N['rest']>
|
|
69
|
-
: PropsFromChildren<N['rest']>
|
|
70
|
+
: CallIfKeep<List<Node['list']>, PropsFromChildren<Node['list']>>
|
|
70
71
|
>
|
|
71
72
|
|
|
72
|
-
type
|
|
73
|
-
|
|
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 []
|
|
74
76
|
? (search?: SParams) => string
|
|
75
|
-
:
|
|
76
|
-
? ((search?: SParams) => string) & PropsFromChildren<Rest>
|
|
77
|
-
: PropsFromChildren<Rest>
|
|
77
|
+
: CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
|
|
78
78
|
>
|
|
79
79
|
|
|
80
|
-
type
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
type WrapResult<Rest extends readonly PathDef[]> = WithWhen<
|
|
85
|
-
HasKeep<Rest> extends true
|
|
86
|
-
? ((search?: SParams) => string) & PropsFromChildren<Rest>
|
|
87
|
-
: PropsFromChildren<Rest>
|
|
88
|
-
>
|
|
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']>>>
|
|
89
83
|
|
|
90
|
-
type
|
|
84
|
+
type RouteFromPick<P extends Pick<any, any, any, any>> = (
|
|
85
|
+
val: PickKey<P['mode']>
|
|
86
|
+
) => RoutesFromDefs<P['list']>
|
|
91
87
|
|
|
92
88
|
type RoutesFromDefs<Defs extends readonly PathDef[]> = WithWhen<
|
|
93
|
-
|
|
94
|
-
? ((search?: SParams) => string) & PropsFromChildren<Defs>
|
|
95
|
-
: PropsFromChildren<Defs>
|
|
89
|
+
CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
|
|
96
90
|
>
|
|
97
91
|
|
|
98
|
-
type KeyFromSeg<S extends string> = KebabToCamel<S>;
|
|
99
92
|
declare const keep: () => Keep;
|
|
100
|
-
declare const path: <const Name extends string, const
|
|
101
|
-
declare const slot: <const Name extends string, const
|
|
102
|
-
declare const wrap: <const Name extends string, const
|
|
103
|
-
declare
|
|
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>;
|
|
104
98
|
|
|
105
|
-
export { keep, path, root, slot, wrap };
|
|
99
|
+
export { keep, path, pick, root, slot, wrap };
|
package/dist/api.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// ---------- Helpers ----------------------
|
|
2
|
-
type
|
|
3
|
-
|
|
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>>}`
|
|
4
7
|
: S
|
|
5
8
|
|
|
6
9
|
// ---------- Shared public types ----------
|
|
@@ -13,93 +16,84 @@ type Keep = { kind: 'keep' }
|
|
|
13
16
|
type Path<
|
|
14
17
|
Name extends string = string,
|
|
15
18
|
Uuid extends string = string,
|
|
16
|
-
|
|
17
|
-
> = { kind: 'path'; name: Name; uuid: Uuid;
|
|
19
|
+
List extends readonly PathDef[] = readonly PathDef[],
|
|
20
|
+
> = { kind: 'path'; name: Name; uuid: Uuid; list: List }
|
|
18
21
|
|
|
19
22
|
type Slot<
|
|
20
23
|
Name extends string = string,
|
|
21
24
|
Uuid extends string = string,
|
|
22
|
-
|
|
23
|
-
> = { kind: 'slot'; name: Name; uuid: Uuid;
|
|
25
|
+
List extends readonly PathDef[] = readonly PathDef[],
|
|
26
|
+
> = { kind: 'slot'; name: Name; uuid: Uuid; list: List }
|
|
24
27
|
|
|
25
28
|
type Wrap<
|
|
26
29
|
Name extends string = string,
|
|
27
30
|
Uuid extends string = string,
|
|
28
|
-
|
|
31
|
+
List extends readonly PathDef[] = readonly PathDef[],
|
|
29
32
|
Args = unknown,
|
|
30
|
-
> = { kind: 'wrap'; name: Name; uuid: Uuid;
|
|
33
|
+
> = { kind: 'wrap'; name: Name; uuid: Uuid; list: List; when: (args: Args) => boolean }
|
|
31
34
|
|
|
32
|
-
type
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
37
42
|
|
|
38
43
|
// ---------- Type-level route builder ----------
|
|
39
|
-
|
|
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 & {
|
|
40
49
|
$when(cond: boolean, seg: Segment | readonly Segment[]): this
|
|
41
50
|
$join(seg: Segment | readonly Segment[]): this
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
type
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
type NonKeepChildren<Rest extends readonly PathDef[]> = Exclude<Rest[number], Keep>
|
|
48
|
-
|
|
49
|
-
type PropsFromChildren<Rest extends readonly PathDef[]> = {
|
|
50
|
-
[C in NonKeepChildren<Rest> as C extends { name: infer N extends string }
|
|
51
|
-
? N
|
|
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>
|
|
52
56
|
: never]: C extends Path<any, any, any>
|
|
53
57
|
? RouteFromPath<C>
|
|
54
58
|
: C extends Slot<any, any, any>
|
|
55
59
|
? RouteFromSlot<C>
|
|
56
60
|
: C extends Wrap<any, any, any, any>
|
|
57
61
|
? RouteFromWrap<C>
|
|
58
|
-
:
|
|
62
|
+
: C extends Pick<any, any, any, any>
|
|
63
|
+
? RouteFromPick<C>
|
|
64
|
+
: never
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
type
|
|
62
|
-
|
|
63
|
-
// Example: apply it to the outputs
|
|
64
|
-
type RouteFromPath<N extends Path<any, any, any>> = WithWhen<
|
|
65
|
-
N['rest'] extends readonly []
|
|
67
|
+
type RouteFromPath<Node extends Path<any, any, any>> = WithWhen<
|
|
68
|
+
Node['list'] extends readonly []
|
|
66
69
|
? (search?: SParams) => string
|
|
67
|
-
:
|
|
68
|
-
? ((search?: SParams) => string) & PropsFromChildren<N['rest']>
|
|
69
|
-
: PropsFromChildren<N['rest']>
|
|
70
|
+
: CallIfKeep<List<Node['list']>, PropsFromChildren<Node['list']>>
|
|
70
71
|
>
|
|
71
72
|
|
|
72
|
-
type
|
|
73
|
-
|
|
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 []
|
|
74
76
|
? (search?: SParams) => string
|
|
75
|
-
:
|
|
76
|
-
? ((search?: SParams) => string) & PropsFromChildren<Rest>
|
|
77
|
-
: PropsFromChildren<Rest>
|
|
77
|
+
: CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
|
|
78
78
|
>
|
|
79
79
|
|
|
80
|
-
type
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
type WrapResult<Rest extends readonly PathDef[]> = WithWhen<
|
|
85
|
-
HasKeep<Rest> extends true
|
|
86
|
-
? ((search?: SParams) => string) & PropsFromChildren<Rest>
|
|
87
|
-
: PropsFromChildren<Rest>
|
|
88
|
-
>
|
|
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']>>>
|
|
89
83
|
|
|
90
|
-
type
|
|
84
|
+
type RouteFromPick<P extends Pick<any, any, any, any>> = (
|
|
85
|
+
val: PickKey<P['mode']>
|
|
86
|
+
) => RoutesFromDefs<P['list']>
|
|
91
87
|
|
|
92
88
|
type RoutesFromDefs<Defs extends readonly PathDef[]> = WithWhen<
|
|
93
|
-
|
|
94
|
-
? ((search?: SParams) => string) & PropsFromChildren<Defs>
|
|
95
|
-
: PropsFromChildren<Defs>
|
|
89
|
+
CallIfKeep<List<Defs>, PropsFromChildren<Defs>>
|
|
96
90
|
>
|
|
97
91
|
|
|
98
|
-
type KeyFromSeg<S extends string> = KebabToCamel<S>;
|
|
99
92
|
declare const keep: () => Keep;
|
|
100
|
-
declare const path: <const Name extends string, const
|
|
101
|
-
declare const slot: <const Name extends string, const
|
|
102
|
-
declare const wrap: <const Name extends string, const
|
|
103
|
-
declare
|
|
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>;
|
|
104
98
|
|
|
105
|
-
export { keep, path, root, slot, wrap };
|
|
99
|
+
export { keep, path, pick, root, slot, wrap };
|
package/dist/api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a,b,c,d,e}from"./chunk-
|
|
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
|
|
@@ -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"]}
|
package/dist/dialect-node.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
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
2
|
//# sourceMappingURL=dialect-node.cjs.map
|
|
@@ -1 +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 } from './api'\n","import {\n\tKebabToCamel,\n\tKeep,\n\tPath,\n\tPathDef,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamel = (s: string) => s.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\ntype KeyFromSeg<S extends string> = KebabToCamel<S>\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\twhen,\n\trest: (rest ?? []) as Rest,\n})\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap',\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// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path.filter(Boolean).join('/').replace('/\\/{2,}/g', '/')}${search ? `?${search}` : ''}`\n\n// ---------- Typed root signature ----------\nexport function root<const Defs extends readonly PathDef[]>(defs: Defs): 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: any) => c.kind === 'keep')\n\tconst allPath =\n\t\tdef.kind === 'slot' || def.kind === 'wrap'\n\t\t\t? prefix\n\t\t\t: def.uuid\n\t\t\t\t? [...prefix, def.uuid]\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)\n\t\t? (search?: SParams) => url(allPath, search)\n\t\t: Object.create(null)\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.uuid] = (param: Segment) => {\n\t\t\t\t\tconst leafPath = [...allPath, param]\n\t\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\t\treturn attachWhenAndJoin(fn, leafPath, [])\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = (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: Object.create(null),\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\tconst leafPath = [...allPath, child.uuid]\n\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(fn, leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildPath(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = (arg: unknown) => {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.uuid] : 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)\n\t\t\t\t\t\t? (search?: SParams) => url(wrapped, search)\n\t\t\t\t\t\t: Object.create(null),\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\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 (rest.length === 0 && typeof target === 'function') {\n\t\t\tconst leaf: any = (search?: SParams) => url(nextPath, search)\n\t\t\treturn attachWhenAndJoin(leaf, nextPath, rest)\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 buildPath(nextPath, path('', rest))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = (seg: Segment | readonly Segment[]) => when(true, seg)\n\n\treturn target\n}\n"],"mappings":"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","names":["dialect_node_exports","__export","keep","slot","root","wrap","path","__toCommonJS","toCamel","s","_","c","keep","path","name","rest","assertValidName","slot","wrap","when","IDENT","kind","url","search","root","defs","buildPath","prefix","def","hasKeep","pathDef","allPath","target","child","param","leafPath","attachWhenAndJoin","subTree","fn","arg","wrapped","basePath","cond","seg","nextPath"]}
|
|
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"]}
|
package/dist/dialect-node.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { keep as base, slot as bind, root as link, wrap as mask, path as node } from './api.cjs';
|
|
1
|
+
export { keep as base, slot as bind, root as link, wrap as mask, path as node, pick } from './api.cjs';
|
package/dist/dialect-node.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { keep as base, slot as bind, root as link, wrap as mask, path as node } from './api.js';
|
|
1
|
+
export { keep as base, slot as bind, root as link, wrap as mask, path as node, pick } from './api.js';
|
package/dist/dialect-node.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a,b as s,c as o,d as e,e as p}from"./chunk-
|
|
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
2
|
//# sourceMappingURL=dialect-node.js.map
|
package/dist/dialect-path.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
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
2
|
//# sourceMappingURL=dialect-path.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dialect-path.ts","../src/api.ts"],"sourcesContent":["export { root, path, slot, keep, wrap } from './api'\n","import {\n\
|
|
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":"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_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"]}
|
package/dist/dialect-path.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { keep, path, root, slot, wrap } from './api.cjs';
|
|
1
|
+
export { keep, path, pick, root, slot, wrap } from './api.cjs';
|
package/dist/dialect-path.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { keep, path, root, slot, wrap } from './api.js';
|
|
1
|
+
export { keep, path, pick, root, slot, wrap } from './api.js';
|
package/dist/dialect-path.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as o,b as p,c as r,d as t,e}from"./chunk-
|
|
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
2
|
//# sourceMappingURL=dialect-path.js.map
|
package/dist/dialect-step.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
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
2
|
//# sourceMappingURL=dialect-step.cjs.map
|
|
@@ -1 +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 } from './api'\n","import {\n\tKebabToCamel,\n\tKeep,\n\tPath,\n\tPathDef,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamel = (s: string) => s.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\ntype KeyFromSeg<S extends string> = KebabToCamel<S>\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\twhen,\n\trest: (rest ?? []) as Rest,\n})\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap',\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// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path.filter(Boolean).join('/').replace('/\\/{2,}/g', '/')}${search ? `?${search}` : ''}`\n\n// ---------- Typed root signature ----------\nexport function root<const Defs extends readonly PathDef[]>(defs: Defs): 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: any) => c.kind === 'keep')\n\tconst allPath =\n\t\tdef.kind === 'slot' || def.kind === 'wrap'\n\t\t\t? prefix\n\t\t\t: def.uuid\n\t\t\t\t? [...prefix, def.uuid]\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)\n\t\t? (search?: SParams) => url(allPath, search)\n\t\t: Object.create(null)\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.uuid] = (param: Segment) => {\n\t\t\t\t\tconst leafPath = [...allPath, param]\n\t\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\t\treturn attachWhenAndJoin(fn, leafPath, [])\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = (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: Object.create(null),\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\tconst leafPath = [...allPath, child.uuid]\n\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(fn, leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildPath(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = (arg: unknown) => {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.uuid] : 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)\n\t\t\t\t\t\t? (search?: SParams) => url(wrapped, search)\n\t\t\t\t\t\t: Object.create(null),\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\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 (rest.length === 0 && typeof target === 'function') {\n\t\t\tconst leaf: any = (search?: SParams) => url(nextPath, search)\n\t\t\treturn attachWhenAndJoin(leaf, nextPath, rest)\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 buildPath(nextPath, path('', rest))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = (seg: Segment | readonly Segment[]) => when(true, seg)\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,GCcA,IAAMQ,EAAWC,GAAcA,EAAE,QAAQ,kBAAmB,CAACC,EAAGC,IAAMA,EAAE,YAAY,CAAC,EAIxEC,EAAO,KAAa,CAAE,KAAM,MAAO,GAEnCC,EAAO,CAInBC,EACAC,KACyC,CACzC,KAAM,OACN,KAAMC,EAAgB,OAAQF,CAAI,EAClC,KAAMN,EAAQM,CAAI,EAClB,KAAOC,GAAQ,CAAC,CACjB,GAEaE,EAAO,CAInBH,EACAC,KACyC,CACzC,KAAM,OACN,KAAMC,EAAgB,OAAQF,CAAI,EAClC,KAAMN,EAAQM,CAAI,EAClB,KAAOC,GAAQ,CAAC,CACjB,GAEaG,EAAO,CAKnBJ,EACAK,EACAJ,KAC+C,CAC/C,KAAM,OACN,KAAMC,EAAgB,OAAQF,CAAI,EAClC,KAAMN,EAAQM,CAAI,EAClB,KAAAK,EACA,KAAOJ,GAAQ,CAAC,CACjB,GAEMK,EAAQ,2BAEd,SAASJ,EACRK,EACAP,EACO,CAEP,GAAIO,IAAS,QAAUP,IAAS,GAAI,OAAOA,EAAK,KAAK,EAErD,GAAI,CAACA,EAAM,MAAM,IAAI,MAAM,GAAGO,CAAI,uBAAuB,EACzD,GAAI,CAACD,EAAM,KAAKN,CAAI,EACnB,MAAM,IAAI,MACT,GAAGO,CAAI,UAAUP,CAAI,+EACtB,EAGD,OAAOA,EAAK,KAAK,CAClB,CAGA,IAAMQ,EAAM,CAACT,EAAiBU,IAC7B,IAAIV,EAAK,OAAO,OAAO,EAAE,KAAK,GAAG,EAAE,QAAQ,WAAa,GAAG,CAAC,GAAGU,EAAS,IAAIA,CAAM,GAAK,EAAE,GAGnF,SAASC,EAA4CC,EAAkC,CAC7F,OAAOC,EAAU,CAAC,EAAGb,EAAK,GAAIY,CAAI,CAAC,CACpC,CAEA,SAASC,EAAUC,EAAmBC,EAAc,CACnD,IAAMC,EAAWC,GAAqBA,EAAQ,KAAK,KAAMnB,GAAWA,EAAE,OAAS,MAAM,EAC/EoB,EACLH,EAAI,OAAS,QAAUA,EAAI,OAAS,OACjCD,EACAC,EAAI,KACH,CAAC,GAAGD,EAAQC,EAAI,IAAI,EACpBD,EAGCK,EAAcH,EAAQD,CAAG,EAC3BL,GAAqBD,EAAIS,EAASR,CAAM,EACzC,OAAO,OAAO,IAAI,EAErB,QAAWU,KAASL,EAAI,KACvB,GAAIK,EAAM,OAAS,OACdA,EAAM,KAAK,SAAW,EACzBD,EAAOC,EAAM,IAAI,EAAKC,GAAmB,CACxC,IAAMC,EAAW,CAAC,GAAGJ,EAASG,CAAK,EAEnC,OAAOE,EADUb,GAAqBD,EAAIa,EAAUZ,CAAM,EAC7BY,EAAU,CAAC,CAAC,CAC1C,EAEAH,EAAOC,EAAM,IAAI,EAAKC,GAAmB,CAGxC,IAAMG,EAAUX,EAAU,CAAC,GAAGK,EAASG,CAAK,EAAGD,CAAK,EAGpD,OAAO,OAAO,OACbJ,EAAQI,CAAK,EACTV,GAAqBD,EAAI,CAAC,GAAGS,EAASG,CAAK,EAAGX,CAAM,EACrD,OAAO,OAAO,IAAI,EACrBc,CACD,CACD,UAESJ,EAAM,OAAS,OACzB,GAAIA,EAAM,KAAK,SAAW,EAAG,CAC5B,IAAME,EAAW,CAAC,GAAGJ,EAASE,EAAM,IAAI,EAClCK,EAAWf,GAAqBD,EAAIa,EAAUZ,CAAM,EAC1DS,EAAOC,EAAM,IAAI,EAAIG,EAAkBE,EAAIH,EAAU,CAAC,CAAC,CACxD,MACCH,EAAOC,EAAM,IAAI,EAAIP,EAAUK,EAASE,CAAK,OAEpCA,EAAM,OAAS,SACzBD,EAAOC,EAAM,IAAI,EAAKM,GAAiB,CAEtC,IAAMC,EADUP,EAAM,KAAKM,CAAG,EACJ,CAAC,GAAGR,EAASE,EAAM,IAAI,EAAIF,EAC/CM,EAAUX,EAAUc,EAASP,CAAY,EAE/C,OAAO,OAAO,OAEbJ,EAAQI,CAAY,EAChBV,GAAqBD,EAAIkB,EAASjB,CAAM,EACzC,OAAO,OAAO,IAAI,EACrBc,CACD,CACD,GAIF,OAAOD,EAAkBJ,EAAQD,EAASH,EAAI,IAAI,CACnD,CAEA,SAASQ,EAAkBJ,EAAaS,EAAqB1B,EAA0B,CACtF,IAAMI,EAAO,CAACuB,EAAeC,IAAsC,CAClE,IAAMC,EAAWF,EAAO,CAAC,GAAGD,EAAU,GAAI,MAAM,QAAQE,CAAG,EAAIA,EAAM,CAACA,CAAG,CAAE,EAAIF,EAG/E,OAAI1B,EAAK,SAAW,GAAK,OAAOiB,GAAW,WAEnCI,EADYb,GAAqBD,EAAIsB,EAAUrB,CAAM,EAC7BqB,EAAU7B,CAAI,EAKvCW,EAAUkB,EAAU/B,EAAK,GAAIE,CAAI,CAAC,CAC1C,EAEA,OAAAiB,EAAO,MAAQb,EACfa,EAAO,MAASW,GAAsCxB,EAAK,GAAMwB,CAAG,EAE7DX,CACR","names":["dialect_step_exports","__export","wrap","slot","root","keep","path","__toCommonJS","toCamel","s","_","c","keep","path","name","rest","assertValidName","slot","wrap","when","IDENT","kind","url","search","root","defs","buildPath","prefix","def","hasKeep","pathDef","allPath","target","child","param","leafPath","attachWhenAndJoin","subTree","fn","arg","wrapped","basePath","cond","seg","nextPath"]}
|
|
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"]}
|
package/dist/dialect-step.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { wrap as gate, slot as item, root as make, keep as self, path as step } from './api.cjs';
|
|
1
|
+
export { wrap as gate, slot as item, root as make, pick, keep as self, path as step } from './api.cjs';
|
package/dist/dialect-step.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { wrap as gate, slot as item, root as make, keep as self, path as step } from './api.js';
|
|
1
|
+
export { wrap as gate, slot as item, root as make, pick, keep as self, path as step } from './api.js';
|
package/dist/dialect-step.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a,b as e,c as s,d as t,e as o}from"./chunk-
|
|
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
2
|
//# sourceMappingURL=dialect-step.js.map
|
package/dist/dialect-tree.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
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
2
|
//# sourceMappingURL=dialect-tree.cjs.map
|
|
@@ -1 +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 } from './api'\n","import {\n\tKebabToCamel,\n\tKeep,\n\tPath,\n\tPathDef,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamel = (s: string) => s.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\ntype KeyFromSeg<S extends string> = KebabToCamel<S>\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\twhen,\n\trest: (rest ?? []) as Rest,\n})\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap',\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// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path.filter(Boolean).join('/').replace('/\\/{2,}/g', '/')}${search ? `?${search}` : ''}`\n\n// ---------- Typed root signature ----------\nexport function root<const Defs extends readonly PathDef[]>(defs: Defs): 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: any) => c.kind === 'keep')\n\tconst allPath =\n\t\tdef.kind === 'slot' || def.kind === 'wrap'\n\t\t\t? prefix\n\t\t\t: def.uuid\n\t\t\t\t? [...prefix, def.uuid]\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)\n\t\t? (search?: SParams) => url(allPath, search)\n\t\t: Object.create(null)\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.uuid] = (param: Segment) => {\n\t\t\t\t\tconst leafPath = [...allPath, param]\n\t\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\t\treturn attachWhenAndJoin(fn, leafPath, [])\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = (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: Object.create(null),\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\tconst leafPath = [...allPath, child.uuid]\n\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(fn, leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildPath(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = (arg: unknown) => {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.uuid] : 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)\n\t\t\t\t\t\t? (search?: SParams) => url(wrapped, search)\n\t\t\t\t\t\t: Object.create(null),\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\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 (rest.length === 0 && typeof target === 'function') {\n\t\t\tconst leaf: any = (search?: SParams) => url(nextPath, search)\n\t\t\treturn attachWhenAndJoin(leaf, nextPath, rest)\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 buildPath(nextPath, path('', rest))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = (seg: Segment | readonly Segment[]) => when(true, seg)\n\n\treturn target\n}\n"],"mappings":"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","names":["dialect_tree_exports","__export","root","wrap","slot","path","keep","__toCommonJS","toCamel","s","_","c","keep","path","name","rest","assertValidName","slot","wrap","when","IDENT","kind","url","search","root","defs","buildPath","prefix","def","hasKeep","pathDef","allPath","target","child","param","leafPath","attachWhenAndJoin","subTree","fn","arg","wrapped","basePath","cond","seg","nextPath"]}
|
|
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":"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","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"]}
|
package/dist/dialect-tree.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { root as grow, wrap as nest, slot as seed, path as tree, keep as twig } from './api.cjs';
|
|
1
|
+
export { root as grow, wrap as nest, pick, slot as seed, path as tree, keep as twig } from './api.cjs';
|
package/dist/dialect-tree.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { root as grow, wrap as nest, slot as seed, path as tree, keep as twig } from './api.js';
|
|
1
|
+
export { root as grow, wrap as nest, pick, slot as seed, path as tree, keep as twig } from './api.js';
|
package/dist/dialect-tree.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e,b as s,c as a,d as t,e as o}from"./chunk-
|
|
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
2
|
//# sourceMappingURL=dialect-tree.js.map
|
package/package.json
CHANGED
package/dist/chunk-5ZW66SPO.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var f=e=>e.replace(/-([a-zA-Z0-9])/g,(t,r)=>r.toUpperCase()),y=()=>({kind:"keep"}),p=(e,t)=>({kind:"path",name:g("path",e),uuid:f(e),rest:t??[]}),N=(e,t)=>({kind:"slot",name:g("slot",e),uuid:f(e),rest:t??[]}),P=(e,t,r)=>({kind:"wrap",name:g("wrap",e),uuid:f(e),when:t,rest:r??[]}),h=/^[A-Za-z_][A-Za-z0-9_]*$/;function g(e,t){if(e==="path"&&t==="")return t.trim();if(!t)throw new Error(`${e} name cannot be empty`);if(!h.test(t))throw new Error(`${e} name "${t}" must be a valid identifier (letters/digits/_/$, not starting with a digit).`);return t.trim()}var m=(e,t)=>`/${e.filter(Boolean).join("/").replace("//{2,}/g","/")}${t?`?${t}`:""}`;function w(e){return u([],p("",e))}function u(e,t){let r=n=>n.rest.some(a=>a.kind==="keep"),s=t.kind==="slot"||t.kind==="wrap"?e:t.uuid?[...e,t.uuid]:e,o=r(t)?n=>m(s,n):Object.create(null);for(let n of t.rest)if(n.kind==="slot")n.rest.length===0?o[n.uuid]=a=>{let l=[...s,a];return c(d=>m(l,d),l,[])}:o[n.uuid]=a=>{let l=u([...s,a],n);return Object.assign(r(n)?i=>m([...s,a],i):Object.create(null),l)};else if(n.kind==="path")if(n.rest.length===0){let a=[...s,n.uuid],l=i=>m(a,i);o[n.uuid]=c(l,a,[])}else o[n.uuid]=u(s,n);else n.kind==="wrap"&&(o[n.uuid]=a=>{let i=n.when(a)?[...s,n.uuid]:s,d=u(i,n);return Object.assign(r(n)?S=>m(i,S):Object.create(null),d)});return c(o,s,t.rest)}function c(e,t,r){let s=(o,n)=>{let a=o?[...t,...Array.isArray(n)?n:[n]]:t;return r.length===0&&typeof e=="function"?c(i=>m(a,i),a,r):u(a,p("",r))};return e.$when=s,e.$join=o=>s(!0,o),e}export{y as a,p as b,N as c,P as d,w as e};
|
|
2
|
-
//# sourceMappingURL=chunk-5ZW66SPO.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api.ts"],"sourcesContent":["import {\n\tKebabToCamel,\n\tKeep,\n\tPath,\n\tPathDef,\n\tRoutesFromDefs,\n\tSParams,\n\tSegment,\n\tSlot,\n\tSlotDef,\n\tWrap,\n} from './dsl'\n\n// ---------- Transform helpers ------------\nconst toCamel = (s: string) => s.replace(/-([a-zA-Z0-9])/g, (_, c) => c.toUpperCase())\ntype KeyFromSeg<S extends string> = KebabToCamel<S>\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'path',\n\tname: assertValidName('path', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest> => ({\n\tkind: 'slot',\n\tname: assertValidName('slot', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\trest: (rest ?? []) as Rest,\n})\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, KeyFromSeg<Name>, Rest, Args> => ({\n\tkind: 'wrap',\n\tname: assertValidName('wrap', name),\n\tuuid: toCamel(name) as KeyFromSeg<Name>,\n\twhen,\n\trest: (rest ?? []) as Rest,\n})\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/\n\nfunction assertValidName<const Name extends string>(\n\tkind: 'path' | 'slot' | 'wrap',\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// ---------- Runtime implementation ----------\nconst url = (path: Segment[], search?: SParams) =>\n\t`/${path.filter(Boolean).join('/').replace('/\\/{2,}/g', '/')}${search ? `?${search}` : ''}`\n\n// ---------- Typed root signature ----------\nexport function root<const Defs extends readonly PathDef[]>(defs: Defs): 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: any) => c.kind === 'keep')\n\tconst allPath =\n\t\tdef.kind === 'slot' || def.kind === 'wrap'\n\t\t\t? prefix\n\t\t\t: def.uuid\n\t\t\t\t? [...prefix, def.uuid]\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)\n\t\t? (search?: SParams) => url(allPath, search)\n\t\t: Object.create(null)\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.uuid] = (param: Segment) => {\n\t\t\t\t\tconst leafPath = [...allPath, param]\n\t\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\t\treturn attachWhenAndJoin(fn, leafPath, [])\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = (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: Object.create(null),\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\tconst leafPath = [...allPath, child.uuid]\n\t\t\t\tconst fn: any = (search?: SParams) => url(leafPath, search)\n\t\t\t\ttarget[child.uuid] = attachWhenAndJoin(fn, leafPath, [])\n\t\t\t} else {\n\t\t\t\ttarget[child.uuid] = buildPath(allPath, child)\n\t\t\t}\n\t\t} else if (child.kind === 'wrap') {\n\t\t\ttarget[child.uuid] = (arg: unknown) => {\n\t\t\t\tconst enabled = child.when(arg)\n\t\t\t\tconst wrapped = enabled ? [...allPath, child.uuid] : 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)\n\t\t\t\t\t\t? (search?: SParams) => url(wrapped, search)\n\t\t\t\t\t\t: Object.create(null),\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\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 (rest.length === 0 && typeof target === 'function') {\n\t\t\tconst leaf: any = (search?: SParams) => url(nextPath, search)\n\t\t\treturn attachWhenAndJoin(leaf, nextPath, rest)\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 buildPath(nextPath, path('', rest))\n\t}\n\n\ttarget.$when = when\n\ttarget.$join = (seg: Segment | readonly Segment[]) => when(true, seg)\n\n\treturn target\n}\n"],"mappings":"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","names":["toCamel","s","_","c","keep","path","name","rest","assertValidName","slot","wrap","when","IDENT","kind","url","search","root","defs","buildPath","prefix","def","hasKeep","pathDef","allPath","target","child","param","leafPath","attachWhenAndJoin","subTree","fn","arg","wrapped","basePath","cond","seg","nextPath"]}
|