@piqit/resolvers 0.0.2 → 0.0.3
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/package.json +9 -9
- package/src/edge.ts +0 -15
- package/src/file-markdown.ts +0 -352
- package/src/frontmatter.ts +0 -269
- package/src/index.ts +0 -65
- package/src/markdown.ts +0 -262
- package/src/path-pattern.ts +0 -226
- package/src/static.ts +0 -227
package/src/index.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @piqit/resolvers - Resolver implementations for piq v2
|
|
3
|
-
*
|
|
4
|
-
* Provides resolvers for querying structured content:
|
|
5
|
-
* - fileMarkdown: Query markdown files from filesystem (Node.js/Bun)
|
|
6
|
-
* - staticContent: Query pre-compiled content (Edge/Workers)
|
|
7
|
-
*
|
|
8
|
-
* @packageDocumentation
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// =============================================================================
|
|
12
|
-
// Filesystem Resolver (Node.js/Bun only)
|
|
13
|
-
// =============================================================================
|
|
14
|
-
|
|
15
|
-
export { fileMarkdown } from "./file-markdown"
|
|
16
|
-
export type { FileMarkdownOptions, FileMarkdownResult } from "./file-markdown"
|
|
17
|
-
|
|
18
|
-
// =============================================================================
|
|
19
|
-
// Static Content Resolver (Edge/Workers compatible)
|
|
20
|
-
// =============================================================================
|
|
21
|
-
|
|
22
|
-
export { staticContent, staticResolver } from "./static"
|
|
23
|
-
|
|
24
|
-
// =============================================================================
|
|
25
|
-
// Path Pattern Utilities
|
|
26
|
-
// =============================================================================
|
|
27
|
-
|
|
28
|
-
export { compilePattern, createParamsSchema } from "./path-pattern"
|
|
29
|
-
export type {
|
|
30
|
-
CompiledPattern,
|
|
31
|
-
ExtractParams,
|
|
32
|
-
PathParams,
|
|
33
|
-
} from "./path-pattern"
|
|
34
|
-
|
|
35
|
-
// =============================================================================
|
|
36
|
-
// Frontmatter Utilities
|
|
37
|
-
// =============================================================================
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
parseFrontmatter,
|
|
41
|
-
extractFrontmatterString,
|
|
42
|
-
getFrontmatterEndOffset,
|
|
43
|
-
readFrontmatter,
|
|
44
|
-
readFrontmatterWithOffset,
|
|
45
|
-
} from "./frontmatter"
|
|
46
|
-
|
|
47
|
-
// =============================================================================
|
|
48
|
-
// Markdown Utilities
|
|
49
|
-
// =============================================================================
|
|
50
|
-
|
|
51
|
-
export {
|
|
52
|
-
parseMarkdownBody,
|
|
53
|
-
extractHeadings,
|
|
54
|
-
slugify,
|
|
55
|
-
markdownToHtml,
|
|
56
|
-
readMarkdownBody,
|
|
57
|
-
readParsedBody,
|
|
58
|
-
} from "./markdown"
|
|
59
|
-
|
|
60
|
-
export type {
|
|
61
|
-
BodyOptions,
|
|
62
|
-
BodyResult,
|
|
63
|
-
BodyShape,
|
|
64
|
-
Heading,
|
|
65
|
-
} from "./markdown"
|
package/src/markdown.ts
DELETED
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Markdown Body Parsing
|
|
3
|
-
*
|
|
4
|
-
* Parses markdown body content to extract:
|
|
5
|
-
* - Raw markdown text
|
|
6
|
-
* - HTML (basic conversion)
|
|
7
|
-
* - Headings with slugs
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { getFrontmatterEndOffset } from "./frontmatter"
|
|
11
|
-
|
|
12
|
-
// =============================================================================
|
|
13
|
-
// Types
|
|
14
|
-
// =============================================================================
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Options for parsing markdown body.
|
|
18
|
-
*/
|
|
19
|
-
export interface BodyOptions {
|
|
20
|
-
/** Include raw markdown text */
|
|
21
|
-
raw?: boolean
|
|
22
|
-
/** Include HTML conversion */
|
|
23
|
-
html?: boolean
|
|
24
|
-
/** Extract headings with depth and slugs */
|
|
25
|
-
headings?: boolean
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* A heading extracted from markdown.
|
|
30
|
-
*/
|
|
31
|
-
export interface Heading {
|
|
32
|
-
/** Heading depth (1-6) */
|
|
33
|
-
depth: number
|
|
34
|
-
/** Heading text content */
|
|
35
|
-
text: string
|
|
36
|
-
/** URL-safe slug */
|
|
37
|
-
slug: string
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Result of parsing markdown body.
|
|
42
|
-
*/
|
|
43
|
-
export interface BodyResult {
|
|
44
|
-
/** Raw markdown text (if requested) */
|
|
45
|
-
raw?: string
|
|
46
|
-
/** HTML content (if requested) */
|
|
47
|
-
html?: string
|
|
48
|
-
/** Extracted headings (if requested) */
|
|
49
|
-
headings?: Heading[]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Type-level shape of body result based on options.
|
|
54
|
-
*/
|
|
55
|
-
export type BodyShape<T extends BodyOptions> = {
|
|
56
|
-
[K in keyof T as T[K] extends true ? K : never]: K extends "raw"
|
|
57
|
-
? string
|
|
58
|
-
: K extends "html"
|
|
59
|
-
? string
|
|
60
|
-
: K extends "headings"
|
|
61
|
-
? Heading[]
|
|
62
|
-
: never
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// =============================================================================
|
|
66
|
-
// Markdown Parsing
|
|
67
|
-
// =============================================================================
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Parse markdown body content.
|
|
71
|
-
*
|
|
72
|
-
* @param content - The full file content (including frontmatter if present)
|
|
73
|
-
* @param options - What to extract from the markdown
|
|
74
|
-
* @returns The requested body parts
|
|
75
|
-
*/
|
|
76
|
-
export function parseMarkdownBody(content: string, options: BodyOptions): BodyResult {
|
|
77
|
-
// Strip frontmatter if present
|
|
78
|
-
const bodyOffset = getFrontmatterEndOffset(content)
|
|
79
|
-
const rawBody = content.slice(bodyOffset).replace(/^[\r\n]+/, "")
|
|
80
|
-
|
|
81
|
-
const result: BodyResult = {}
|
|
82
|
-
|
|
83
|
-
if (options.raw) {
|
|
84
|
-
result.raw = rawBody
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (options.html) {
|
|
88
|
-
result.html = markdownToHtml(rawBody)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (options.headings) {
|
|
92
|
-
result.headings = extractHeadings(rawBody)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return result
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// =============================================================================
|
|
99
|
-
// Heading Extraction
|
|
100
|
-
// =============================================================================
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Regex to match ATX-style headings (# Heading).
|
|
104
|
-
*/
|
|
105
|
-
const HEADING_REGEX = /^(#{1,6})\s+(.+)$/gm
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Extract headings from markdown text.
|
|
109
|
-
*
|
|
110
|
-
* @param markdown - The raw markdown content
|
|
111
|
-
* @returns Array of headings with depth, text, and slug
|
|
112
|
-
*/
|
|
113
|
-
export function extractHeadings(markdown: string): Heading[] {
|
|
114
|
-
const headings: Heading[] = []
|
|
115
|
-
let match: RegExpExecArray | null
|
|
116
|
-
|
|
117
|
-
const regex = new RegExp(HEADING_REGEX.source, "gm")
|
|
118
|
-
while ((match = regex.exec(markdown)) !== null) {
|
|
119
|
-
const depth = match[1].length
|
|
120
|
-
const text = match[2].trim()
|
|
121
|
-
const slug = slugify(text)
|
|
122
|
-
|
|
123
|
-
headings.push({ depth, text, slug })
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return headings
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Convert text to a URL-safe slug.
|
|
131
|
-
*
|
|
132
|
-
* @param text - The text to slugify
|
|
133
|
-
* @returns A URL-safe slug
|
|
134
|
-
*/
|
|
135
|
-
export function slugify(text: string): string {
|
|
136
|
-
return text
|
|
137
|
-
.toLowerCase()
|
|
138
|
-
.replace(/[^\w\s-]/g, "") // Remove non-word chars
|
|
139
|
-
.replace(/\s+/g, "-") // Replace spaces with dashes
|
|
140
|
-
.replace(/--+/g, "-") // Replace multiple dashes
|
|
141
|
-
.replace(/^-|-$/g, "") // Trim dashes from ends
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// =============================================================================
|
|
145
|
-
// Basic Markdown to HTML
|
|
146
|
-
// =============================================================================
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Convert markdown to HTML.
|
|
150
|
-
*
|
|
151
|
-
* This is a basic implementation that handles common patterns.
|
|
152
|
-
* For production use, consider using a proper markdown parser.
|
|
153
|
-
*
|
|
154
|
-
* @param markdown - The raw markdown content
|
|
155
|
-
* @returns HTML string
|
|
156
|
-
*/
|
|
157
|
-
export function markdownToHtml(markdown: string): string {
|
|
158
|
-
let html = markdown
|
|
159
|
-
|
|
160
|
-
// Escape HTML entities first
|
|
161
|
-
html = html
|
|
162
|
-
.replace(/&/g, "&")
|
|
163
|
-
.replace(/</g, "<")
|
|
164
|
-
.replace(/>/g, ">")
|
|
165
|
-
|
|
166
|
-
// Headings (must be on their own line)
|
|
167
|
-
html = html.replace(/^######\s+(.+)$/gm, "<h6>$1</h6>")
|
|
168
|
-
html = html.replace(/^#####\s+(.+)$/gm, "<h5>$1</h5>")
|
|
169
|
-
html = html.replace(/^####\s+(.+)$/gm, "<h4>$1</h4>")
|
|
170
|
-
html = html.replace(/^###\s+(.+)$/gm, "<h3>$1</h3>")
|
|
171
|
-
html = html.replace(/^##\s+(.+)$/gm, "<h2>$1</h2>")
|
|
172
|
-
html = html.replace(/^#\s+(.+)$/gm, "<h1>$1</h1>")
|
|
173
|
-
|
|
174
|
-
// Code blocks (fenced)
|
|
175
|
-
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, _lang, code) => {
|
|
176
|
-
return `<pre><code>${code.trim()}</code></pre>`
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
// Inline code
|
|
180
|
-
html = html.replace(/`([^`]+)`/g, "<code>$1</code>")
|
|
181
|
-
|
|
182
|
-
// Bold
|
|
183
|
-
html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
|
184
|
-
html = html.replace(/__([^_]+)__/g, "<strong>$1</strong>")
|
|
185
|
-
|
|
186
|
-
// Italic
|
|
187
|
-
html = html.replace(/\*([^*]+)\*/g, "<em>$1</em>")
|
|
188
|
-
html = html.replace(/_([^_]+)_/g, "<em>$1</em>")
|
|
189
|
-
|
|
190
|
-
// Links
|
|
191
|
-
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>')
|
|
192
|
-
|
|
193
|
-
// Images
|
|
194
|
-
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">')
|
|
195
|
-
|
|
196
|
-
// Horizontal rules
|
|
197
|
-
html = html.replace(/^---+$/gm, "<hr>")
|
|
198
|
-
html = html.replace(/^\*\*\*+$/gm, "<hr>")
|
|
199
|
-
|
|
200
|
-
// Unordered lists (simplified)
|
|
201
|
-
html = html.replace(/^[-*+]\s+(.+)$/gm, "<li>$1</li>")
|
|
202
|
-
|
|
203
|
-
// Ordered lists (simplified)
|
|
204
|
-
html = html.replace(/^\d+\.\s+(.+)$/gm, "<li>$1</li>")
|
|
205
|
-
|
|
206
|
-
// Wrap consecutive <li> in <ul> or <ol>
|
|
207
|
-
html = html.replace(/((?:<li>.*<\/li>\n?)+)/g, "<ul>\n$1</ul>\n")
|
|
208
|
-
|
|
209
|
-
// Blockquotes
|
|
210
|
-
html = html.replace(/^>\s+(.+)$/gm, "<blockquote>$1</blockquote>")
|
|
211
|
-
|
|
212
|
-
// Paragraphs (wrap remaining text blocks)
|
|
213
|
-
// Split by double newlines and wrap non-block elements
|
|
214
|
-
const blocks = html.split(/\n\n+/)
|
|
215
|
-
html = blocks
|
|
216
|
-
.map((block) => {
|
|
217
|
-
const trimmed = block.trim()
|
|
218
|
-
if (!trimmed) return ""
|
|
219
|
-
// Don't wrap if already a block element
|
|
220
|
-
if (
|
|
221
|
-
/^<(h[1-6]|ul|ol|li|pre|blockquote|hr|p)/.test(trimmed) ||
|
|
222
|
-
/^<\/?(h[1-6]|ul|ol|li|pre|blockquote)>$/.test(trimmed)
|
|
223
|
-
) {
|
|
224
|
-
return trimmed
|
|
225
|
-
}
|
|
226
|
-
return `<p>${trimmed.replace(/\n/g, "<br>")}</p>`
|
|
227
|
-
})
|
|
228
|
-
.filter(Boolean)
|
|
229
|
-
.join("\n")
|
|
230
|
-
|
|
231
|
-
return html
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// =============================================================================
|
|
235
|
-
// File Reading Utilities
|
|
236
|
-
// =============================================================================
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Read just the markdown body from a file (skipping frontmatter).
|
|
240
|
-
*
|
|
241
|
-
* @param path - Path to the markdown file
|
|
242
|
-
* @returns The raw markdown body
|
|
243
|
-
*/
|
|
244
|
-
export async function readMarkdownBody(path: string): Promise<string> {
|
|
245
|
-
const file = Bun.file(path)
|
|
246
|
-
const content = await file.text()
|
|
247
|
-
const bodyOffset = getFrontmatterEndOffset(content)
|
|
248
|
-
return content.slice(bodyOffset).replace(/^\r?\n/, "")
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Read and parse markdown body from a file.
|
|
253
|
-
*
|
|
254
|
-
* @param path - Path to the markdown file
|
|
255
|
-
* @param options - What to extract from the markdown
|
|
256
|
-
* @returns The requested body parts
|
|
257
|
-
*/
|
|
258
|
-
export async function readParsedBody(path: string, options: BodyOptions): Promise<BodyResult> {
|
|
259
|
-
const file = Bun.file(path)
|
|
260
|
-
const content = await file.text()
|
|
261
|
-
return parseMarkdownBody(content, options)
|
|
262
|
-
}
|
package/src/path-pattern.ts
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path Pattern Parsing and Matching
|
|
3
|
-
*
|
|
4
|
-
* Parses patterns like `{year}/{slug}.md` and provides:
|
|
5
|
-
* - Type-level extraction of parameters
|
|
6
|
-
* - Glob pattern generation
|
|
7
|
-
* - Path matching to extract params
|
|
8
|
-
* - Path building from params
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// =============================================================================
|
|
12
|
-
// Type-level Parameter Extraction
|
|
13
|
-
// =============================================================================
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Extract params from a path pattern string at the type level.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* type Params = ExtractParams<'{year}/{slug}.md'>
|
|
20
|
-
* // = { year: string } & { slug: string }
|
|
21
|
-
*/
|
|
22
|
-
export type ExtractParams<P extends string> = P extends `${string}{${infer Param}}${infer Rest}`
|
|
23
|
-
? { [K in Param]: string } & ExtractParams<Rest>
|
|
24
|
-
: {}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Simplify a type by flattening intersections.
|
|
28
|
-
* Converts { a: string } & { b: string } to { a: string; b: string }
|
|
29
|
-
*/
|
|
30
|
-
export type Simplify<T> = { [K in keyof T]: T[K] }
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Extract and simplify params from a path pattern.
|
|
34
|
-
*/
|
|
35
|
-
export type PathParams<P extends string> = Simplify<ExtractParams<P>>
|
|
36
|
-
|
|
37
|
-
// =============================================================================
|
|
38
|
-
// Compiled Pattern Interface
|
|
39
|
-
// =============================================================================
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* A compiled path pattern that can match paths and generate globs.
|
|
43
|
-
*/
|
|
44
|
-
export interface CompiledPattern {
|
|
45
|
-
/**
|
|
46
|
-
* The original pattern string.
|
|
47
|
-
*/
|
|
48
|
-
pattern: string
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* The extracted parameter names.
|
|
52
|
-
*/
|
|
53
|
-
paramNames: string[]
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Generate a glob pattern, optionally constraining specific params.
|
|
57
|
-
*
|
|
58
|
-
* @param constraints - Optional param values to use instead of wildcards
|
|
59
|
-
* @returns A glob pattern string
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* pattern.toGlob() // '** / *.md' (with {year}/{slug}.md)
|
|
63
|
-
* pattern.toGlob({ year: '2024' }) // '2024/*.md'
|
|
64
|
-
*/
|
|
65
|
-
toGlob(constraints?: Record<string, unknown>): string
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Match a path against this pattern and extract params.
|
|
69
|
-
*
|
|
70
|
-
* @param path - The file path to match (relative to base)
|
|
71
|
-
* @returns The extracted params, or null if no match
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* pattern.match('2024/hello-world.md') // { year: '2024', slug: 'hello-world' }
|
|
75
|
-
* pattern.match('invalid') // null
|
|
76
|
-
*/
|
|
77
|
-
match(path: string): Record<string, string> | null
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Build a path from params.
|
|
81
|
-
*
|
|
82
|
-
* @param params - The param values
|
|
83
|
-
* @returns The constructed path
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* pattern.build({ year: '2024', slug: 'hello' }) // '2024/hello.md'
|
|
87
|
-
*/
|
|
88
|
-
build(params: Record<string, string>): string
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// =============================================================================
|
|
92
|
-
// Pattern Parsing
|
|
93
|
-
// =============================================================================
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Regex to match parameter placeholders in patterns.
|
|
97
|
-
* Matches {paramName} where paramName is alphanumeric + underscores.
|
|
98
|
-
*/
|
|
99
|
-
const PARAM_REGEX = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Characters that need escaping in regex.
|
|
103
|
-
*/
|
|
104
|
-
const REGEX_ESCAPE = /[.*+?^${}()|[\]\\]/g
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Compile a path pattern string into a usable pattern object.
|
|
108
|
-
*
|
|
109
|
-
* @param pattern - The pattern string like '{year}/{slug}.md'
|
|
110
|
-
* @returns A CompiledPattern instance
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* const pattern = compilePattern('{year}/{slug}.md')
|
|
114
|
-
* pattern.toGlob() // '** / *.md'
|
|
115
|
-
* pattern.match('2024/hello.md') // { year: '2024', slug: 'hello' }
|
|
116
|
-
*/
|
|
117
|
-
export function compilePattern(pattern: string): CompiledPattern {
|
|
118
|
-
// Extract all parameter names in order
|
|
119
|
-
const paramNames: string[] = []
|
|
120
|
-
let match: RegExpExecArray | null
|
|
121
|
-
const regex = new RegExp(PARAM_REGEX.source, "g")
|
|
122
|
-
while ((match = regex.exec(pattern)) !== null) {
|
|
123
|
-
paramNames.push(match[1])
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Build the match regex by replacing {param} with capturing groups
|
|
127
|
-
// Each param can match anything except path separators
|
|
128
|
-
const regexPattern = pattern
|
|
129
|
-
.replace(REGEX_ESCAPE, (char) => {
|
|
130
|
-
// Don't escape our param placeholders - we'll handle them separately
|
|
131
|
-
if (char === "{" || char === "}") return char
|
|
132
|
-
return "\\" + char
|
|
133
|
-
})
|
|
134
|
-
.replace(PARAM_REGEX, "([^/]+)")
|
|
135
|
-
|
|
136
|
-
const matchRegex = new RegExp(`^${regexPattern}$`)
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
pattern,
|
|
140
|
-
paramNames,
|
|
141
|
-
|
|
142
|
-
toGlob(constraints?: Record<string, unknown>): string {
|
|
143
|
-
let glob = pattern
|
|
144
|
-
|
|
145
|
-
// Replace each param with either its constrained value or a wildcard
|
|
146
|
-
for (const name of paramNames) {
|
|
147
|
-
const value = constraints?.[name]
|
|
148
|
-
if (value !== undefined && value !== null) {
|
|
149
|
-
// Use the constrained value
|
|
150
|
-
glob = glob.replace(`{${name}}`, String(value))
|
|
151
|
-
} else {
|
|
152
|
-
// Use a wildcard - * matches anything except /
|
|
153
|
-
glob = glob.replace(`{${name}}`, "*")
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return glob
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
match(path: string): Record<string, string> | null {
|
|
161
|
-
const result = matchRegex.exec(path)
|
|
162
|
-
if (!result) return null
|
|
163
|
-
|
|
164
|
-
const params: Record<string, string> = {}
|
|
165
|
-
for (let i = 0; i < paramNames.length; i++) {
|
|
166
|
-
params[paramNames[i]] = result[i + 1]
|
|
167
|
-
}
|
|
168
|
-
return params
|
|
169
|
-
},
|
|
170
|
-
|
|
171
|
-
build(params: Record<string, string>): string {
|
|
172
|
-
let result = pattern
|
|
173
|
-
for (const name of paramNames) {
|
|
174
|
-
const value = params[name]
|
|
175
|
-
if (value === undefined) {
|
|
176
|
-
throw new Error(`Missing required param: ${name}`)
|
|
177
|
-
}
|
|
178
|
-
result = result.replace(`{${name}}`, value)
|
|
179
|
-
}
|
|
180
|
-
return result
|
|
181
|
-
},
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// =============================================================================
|
|
186
|
-
// Schema for Path Params
|
|
187
|
-
// =============================================================================
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Create a StandardSchema for path params extracted from a pattern.
|
|
191
|
-
* This is used by the resolver to expose scanParams to piq core.
|
|
192
|
-
*
|
|
193
|
-
* @param pattern - The compiled pattern
|
|
194
|
-
* @returns A StandardSchema that validates param objects
|
|
195
|
-
*/
|
|
196
|
-
export function createParamsSchema(
|
|
197
|
-
pattern: CompiledPattern
|
|
198
|
-
): import("piqit").StandardSchema<Record<string, string>> {
|
|
199
|
-
return {
|
|
200
|
-
"~standard": {
|
|
201
|
-
version: 1,
|
|
202
|
-
vendor: "piqit/resolvers",
|
|
203
|
-
validate(value: unknown) {
|
|
204
|
-
if (value === null || typeof value !== "object") {
|
|
205
|
-
return {
|
|
206
|
-
issues: [{ message: "Expected object" }],
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const obj = value as Record<string, unknown>
|
|
211
|
-
|
|
212
|
-
// All params should be strings if present
|
|
213
|
-
for (const name of pattern.paramNames) {
|
|
214
|
-
const val = obj[name]
|
|
215
|
-
if (val !== undefined && typeof val !== "string") {
|
|
216
|
-
return {
|
|
217
|
-
issues: [{ message: `Param ${name} must be a string`, path: [name] }],
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return { value: obj as Record<string, string> }
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
}
|
|
226
|
-
}
|