foldkit 0.117.0 → 0.118.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/dist/dom/dom.d.ts +10 -0
- package/dist/dom/dom.d.ts.map +1 -1
- package/dist/dom/dom.js +11 -1
- package/dist/route/parser.d.ts +25 -0
- package/dist/route/parser.d.ts.map +1 -1
- package/dist/route/parser.js +61 -1
- package/dist/route/public.d.ts +1 -1
- package/dist/route/public.d.ts.map +1 -1
- package/dist/route/public.js +1 -1
- package/package.json +1 -1
package/dist/dom/dom.d.ts
CHANGED
|
@@ -132,16 +132,26 @@ export declare const scrollIntoViewAfterPaint: (selector: string, options?: Read
|
|
|
132
132
|
* Defaults to `{ block: 'center' }`; pass a different `block` when the
|
|
133
133
|
* target should land at a specific position when it does need to scroll.
|
|
134
134
|
*
|
|
135
|
+
* `when` selects the timing gate. `'Paint'` (the default) waits for
|
|
136
|
+
* `Render.afterPaint`, so the scroll lands after the target is on screen.
|
|
137
|
+
* `'Commit'` waits for `Render.afterCommit` instead, so the scroll lands in
|
|
138
|
+
* the same frame the DOM patch applies, before the browser paints. Use
|
|
139
|
+
* `'Commit'` when the target is brought into view and scrolled by the same
|
|
140
|
+
* Message (such as a menu opening), so it appears already scrolled rather
|
|
141
|
+
* than visibly jumping.
|
|
142
|
+
*
|
|
135
143
|
* Fails with `ElementNotFound` if the selector does not match an `HTMLElement`.
|
|
136
144
|
*
|
|
137
145
|
* @example
|
|
138
146
|
* ```typescript
|
|
139
147
|
* Dom.scrollIntoViewIfNotVisible('#active-nav-link')
|
|
140
148
|
* Dom.scrollIntoViewIfNotVisible('#active-nav-link', { block: 'nearest' })
|
|
149
|
+
* Dom.scrollIntoViewIfNotVisible('#active-nav-link', { when: 'Commit' })
|
|
141
150
|
* ```
|
|
142
151
|
*/
|
|
143
152
|
export declare const scrollIntoViewIfNotVisible: (selector: string, options?: Readonly<{
|
|
144
153
|
block?: ScrollLogicalPosition;
|
|
154
|
+
when?: "Paint" | "Commit";
|
|
145
155
|
}>) => Effect.Effect<void, ElementNotFound>;
|
|
146
156
|
/** Direction for focus advancement: forward or backward in tab order. */
|
|
147
157
|
export type FocusDirection = 'Next' | 'Previous';
|
package/dist/dom/dom.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/dom/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAMP,MAAM,QAAQ,CAAA;AAGf,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAiC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,KAAK,GAChB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,KACvE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAQlC,CAAA;AAEJ;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,KAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAsElC,CAAA;AAuBJ;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAyBlC,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,qBAAqB,CAAA;CAAE,CAAC,KACpD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,GACnC,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,qBAAqB,CAAA;CAAE,CAAC,KACpD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAcJ
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/dom/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAMP,MAAM,QAAQ,CAAA;AAGf,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAiC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,KAAK,GAChB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,KACvE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAQlC,CAAA;AAEJ;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,KAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAsElC,CAAA;AAuBJ;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAyBlC,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,qBAAqB,CAAA;CAAE,CAAC,KACpD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAEJ;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,GACnC,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,qBAAqB,CAAA;CAAE,CAAC,KACpD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAKlC,CAAA;AAcJ;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,0BAA0B,GACrC,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IACjB,KAAK,CAAC,EAAE,qBAAqB,CAAA;IAC7B,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC1B,CAAC,KACD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAuBlC,CAAA;AAEJ,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,CAAA;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,EAChB,WAAW,cAAc,KACxB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAiClC,CAAA"}
|
package/dist/dom/dom.js
CHANGED
|
@@ -250,16 +250,26 @@ const findScrollParent = (element) => {
|
|
|
250
250
|
* Defaults to `{ block: 'center' }`; pass a different `block` when the
|
|
251
251
|
* target should land at a specific position when it does need to scroll.
|
|
252
252
|
*
|
|
253
|
+
* `when` selects the timing gate. `'Paint'` (the default) waits for
|
|
254
|
+
* `Render.afterPaint`, so the scroll lands after the target is on screen.
|
|
255
|
+
* `'Commit'` waits for `Render.afterCommit` instead, so the scroll lands in
|
|
256
|
+
* the same frame the DOM patch applies, before the browser paints. Use
|
|
257
|
+
* `'Commit'` when the target is brought into view and scrolled by the same
|
|
258
|
+
* Message (such as a menu opening), so it appears already scrolled rather
|
|
259
|
+
* than visibly jumping.
|
|
260
|
+
*
|
|
253
261
|
* Fails with `ElementNotFound` if the selector does not match an `HTMLElement`.
|
|
254
262
|
*
|
|
255
263
|
* @example
|
|
256
264
|
* ```typescript
|
|
257
265
|
* Dom.scrollIntoViewIfNotVisible('#active-nav-link')
|
|
258
266
|
* Dom.scrollIntoViewIfNotVisible('#active-nav-link', { block: 'nearest' })
|
|
267
|
+
* Dom.scrollIntoViewIfNotVisible('#active-nav-link', { when: 'Commit' })
|
|
259
268
|
* ```
|
|
260
269
|
*/
|
|
261
270
|
export const scrollIntoViewIfNotVisible = (selector, options) => Effect.gen(function* () {
|
|
262
|
-
|
|
271
|
+
const when = options?.when ?? 'Paint';
|
|
272
|
+
yield* M.value(when).pipe(M.when('Paint', () => afterPaint), M.when('Commit', () => afterCommit), M.exhaustive);
|
|
263
273
|
const element = yield* queryHTMLElement(selector);
|
|
264
274
|
Option.match(findScrollParent(element), {
|
|
265
275
|
onNone: () => element.scrollIntoView({ block: options?.block ?? 'center' }),
|
package/dist/route/parser.d.ts
CHANGED
|
@@ -102,6 +102,31 @@ export declare const string: <K extends string>(name: K) => Biparser<Record<K, s
|
|
|
102
102
|
* ```
|
|
103
103
|
*/
|
|
104
104
|
export declare const int: <K extends string>(name: K) => Biparser<Record<K, number>>;
|
|
105
|
+
export declare const __isSingleSegment: (segment: string) => boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Creates a parser that captures a URL segment and decodes it through an
|
|
108
|
+
* Effect `Schema`, producing a named field of the schema's decoded type.
|
|
109
|
+
*
|
|
110
|
+
* Decodes the raw segment when parsing and encodes the value back when
|
|
111
|
+
* printing, so branded ids, refined strings, and string-literal unions
|
|
112
|
+
* round-trip through the route. The decoded type flows straight into the
|
|
113
|
+
* route value, so a model carries `UserId` rather than a bare `string`.
|
|
114
|
+
*
|
|
115
|
+
* The schema's encoded form must be a single URL segment string. For shapes
|
|
116
|
+
* that span multiple segments or live in the query string, use `rest` or
|
|
117
|
+
* `query`.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* const UserId = S.String.pipe(S.brand('UserId'))
|
|
122
|
+
* pipe(literal('users'), slash(schemaSegment('userId', UserId)), mapTo(UserRoute))
|
|
123
|
+
* // parses /users/abc into { _tag: 'UserRoute', userId: UserId.make('abc') }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @param name - The field name the decoded value is captured under.
|
|
127
|
+
* @param schema - A codec whose encoded form is a single URL segment string.
|
|
128
|
+
*/
|
|
129
|
+
export declare const schemaSegment: <K extends string, A, I extends string>(name: K, schema: Schema.Codec<A, I>) => Biparser<Record<K, A>>;
|
|
105
130
|
/**
|
|
106
131
|
* A parser that matches the root path with no remaining segments.
|
|
107
132
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/route/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAEL,MAAM,EAGN,MAAM,EACN,MAAM,EAIP,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;;;;AAErC;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,gBAA+B;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAC;CAAG;AAEL;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;AAEvD,KAAK,UAAU,GAAG;IAChB,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC/B,WAAW,EAAE,eAAe,CAAA;CAC7B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IACxB,KAAK,EAAE,CACL,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,MAAM,CAAC,EAAE,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;IAC9C,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;CAC9E,CAAA;AAED,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC,MAAM,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,KAAK,GACjC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,GACnC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,GACpC,KAAK,CAAA;AAET;;;;;;;;;GASG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG;IACnC,KAAK,EAAE,CACL,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,MAAM,CAAC,EAAE,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;IAC9C,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CAClB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAA;CAAE,CAAA;AAE3E;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAA;IAC3B,QAAQ,CAAC,6EAA6E,CAAC,EAAE,KAAK,CAAA;CAC/F,CAAA;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/route/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/route/parser.js
CHANGED
|
@@ -10,6 +10,7 @@ export class ParseError extends Data.TaggedError('ParseError') {
|
|
|
10
10
|
const makeTerminalParser = (parser) =>
|
|
11
11
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
12
12
|
parser;
|
|
13
|
+
const pathToSegments = flow(String.split('/'), Array.filter(String.isNonEmpty));
|
|
13
14
|
/**
|
|
14
15
|
* Creates a parser that matches an exact URL path segment.
|
|
15
16
|
*
|
|
@@ -94,6 +95,66 @@ export const int = (name) => param(`integer (${name})`, segment => {
|
|
|
94
95
|
: /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
95
96
|
Effect.succeed({ [name]: parsed });
|
|
96
97
|
}, record => record[name].toString());
|
|
98
|
+
export const __isSingleSegment = (segment) => Array.matchLeft(pathToSegments(segment), {
|
|
99
|
+
onEmpty: () => false,
|
|
100
|
+
onNonEmpty: (head, tail) => head === segment && Array.isArrayEmpty(tail),
|
|
101
|
+
});
|
|
102
|
+
const warnIfNotSingleSegment = (name, segment) => Effect.sync(() => {
|
|
103
|
+
if (import.meta.hot && !__isSingleSegment(segment)) {
|
|
104
|
+
console.warn(`[foldkit] schemaSegment('${name}') encoded to '${segment}', which is not ` +
|
|
105
|
+
'a single URL segment. Parsing re-splits the path on slashes and drops ' +
|
|
106
|
+
'empty pieces, so this value will not round-trip. Encode to one ' +
|
|
107
|
+
'non-empty, slash-free segment, or use rest for multi-segment values.');
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
/**
|
|
111
|
+
* Creates a parser that captures a URL segment and decodes it through an
|
|
112
|
+
* Effect `Schema`, producing a named field of the schema's decoded type.
|
|
113
|
+
*
|
|
114
|
+
* Decodes the raw segment when parsing and encodes the value back when
|
|
115
|
+
* printing, so branded ids, refined strings, and string-literal unions
|
|
116
|
+
* round-trip through the route. The decoded type flows straight into the
|
|
117
|
+
* route value, so a model carries `UserId` rather than a bare `string`.
|
|
118
|
+
*
|
|
119
|
+
* The schema's encoded form must be a single URL segment string. For shapes
|
|
120
|
+
* that span multiple segments or live in the query string, use `rest` or
|
|
121
|
+
* `query`.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const UserId = S.String.pipe(S.brand('UserId'))
|
|
126
|
+
* pipe(literal('users'), slash(schemaSegment('userId', UserId)), mapTo(UserRoute))
|
|
127
|
+
* // parses /users/abc into { _tag: 'UserRoute', userId: UserId.make('abc') }
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @param name - The field name the decoded value is captured under.
|
|
131
|
+
* @param schema - A codec whose encoded form is a single URL segment string.
|
|
132
|
+
*/
|
|
133
|
+
export const schemaSegment = (name, schema) => {
|
|
134
|
+
const decode = Schema.decodeUnknownEffect(schema);
|
|
135
|
+
const encode = Schema.encodeEffect(schema);
|
|
136
|
+
return {
|
|
137
|
+
parse: segments => Array.matchLeft(segments, {
|
|
138
|
+
onEmpty: () => Effect.fail(new ParseError({
|
|
139
|
+
message: `Expected ${name}`,
|
|
140
|
+
expected: name,
|
|
141
|
+
actual: 'end of path',
|
|
142
|
+
position: 0,
|
|
143
|
+
})),
|
|
144
|
+
onNonEmpty: (head, tail) => pipe(head, decode, Effect.mapError(error => new ParseError({
|
|
145
|
+
message: `Invalid ${name}: ${error.message}`,
|
|
146
|
+
expected: name,
|
|
147
|
+
actual: head,
|
|
148
|
+
})), Effect.map(value => [Record.singleton(name, value), tail])),
|
|
149
|
+
}),
|
|
150
|
+
print: (value, state) => pipe(value[name], encode, Effect.mapError(error => new ParseError({
|
|
151
|
+
message: `Failed to encode ${name}: ${error.message}`,
|
|
152
|
+
})), Effect.tap(segment => warnIfNotSingleSegment(name, segment)), Effect.map(segment => ({
|
|
153
|
+
...state,
|
|
154
|
+
segments: [...state.segments, segment],
|
|
155
|
+
}))),
|
|
156
|
+
};
|
|
157
|
+
};
|
|
97
158
|
/**
|
|
98
159
|
* A parser that matches the root path with no remaining segments.
|
|
99
160
|
*
|
|
@@ -275,7 +336,6 @@ export const query = (schema) => (parser) => {
|
|
|
275
336
|
};
|
|
276
337
|
return makeTerminalParser(queryParser);
|
|
277
338
|
};
|
|
278
|
-
const pathToSegments = flow(String.split('/'), Array.filter(String.isNonEmpty));
|
|
279
339
|
const parseUrl = (parser) => (url) => pipe(pathToSegments(url.pathname), segments => parser.parse(segments, Option.getOrUndefined(url.search)), Effect.flatMap(complete), Effect.map(([value]) => value));
|
|
280
340
|
/**
|
|
281
341
|
* Parses a URL against a parser, falling back to a not-found route if no
|
package/dist/route/public.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ParseError, literal, param, string, int, root, rest, oneOf, mapTo, slash, query, parseUrlWithFallback, r, } from './index.js';
|
|
1
|
+
export { ParseError, literal, param, string, int, schemaSegment, root, rest, oneOf, mapTo, slash, query, parseUrlWithFallback, r, } from './index.js';
|
|
2
2
|
export type { ParseResult, Biparser, Router, TerminalParser, ExtendableBiparser, Parser, } from './index.js';
|
|
3
3
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/route/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,OAAO,EACP,KAAK,EACL,MAAM,EACN,GAAG,EACH,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,KAAK,EACL,oBAAoB,EACpB,CAAC,GACF,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,WAAW,EACX,QAAQ,EACR,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,MAAM,GACP,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/route/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,OAAO,EACP,KAAK,EACL,MAAM,EACN,GAAG,EACH,aAAa,EACb,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,KAAK,EACL,oBAAoB,EACpB,CAAC,GACF,MAAM,YAAY,CAAA;AAEnB,YAAY,EACV,WAAW,EACX,QAAQ,EACR,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,MAAM,GACP,MAAM,YAAY,CAAA"}
|
package/dist/route/public.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { ParseError, literal, param, string, int, root, rest, oneOf, mapTo, slash, query, parseUrlWithFallback, r, } from './index.js';
|
|
1
|
+
export { ParseError, literal, param, string, int, schemaSegment, root, rest, oneOf, mapTo, slash, query, parseUrlWithFallback, r, } from './index.js';
|