@mzebley/mark-down 1.2.1 → 1.2.2
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/angular/index.d.ts +18 -0
- package/dist/angular/index.js +58 -0
- package/dist/angular/index.js.map +1 -0
- package/dist/browser.js +580 -0
- package/dist/browser.js.map +1 -0
- package/{src/snippet-client.ts → dist/chunk-35YHML5Z.js} +97 -174
- package/dist/chunk-35YHML5Z.js.map +1 -0
- package/dist/chunk-BRKEJJFQ.js +17 -0
- package/dist/chunk-BRKEJJFQ.js.map +1 -0
- package/dist/chunk-GWLMADTU.js +19 -0
- package/dist/chunk-GWLMADTU.js.map +1 -0
- package/{src/front-matter.ts → dist/chunk-MWZFQXNW.js} +48 -39
- package/dist/chunk-MWZFQXNW.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/inline.d.ts +8 -0
- package/{src/inline.ts → dist/inline.js} +19 -34
- package/dist/inline.js.map +1 -0
- package/dist/mark-down-inline.umd.js +8486 -0
- package/dist/mark-down-inline.umd.js.map +1 -0
- package/dist/slug.d.ts +3 -0
- package/dist/slug.js +8 -0
- package/dist/slug.js.map +1 -0
- package/dist/snippet-client-CiQX2Zcn.d.ts +63 -0
- package/package.json +2 -1
- package/src/angular/index.ts +0 -47
- package/src/browser.ts +0 -141
- package/src/errors.ts +0 -19
- package/src/index.ts +0 -5
- package/src/markdown.ts +0 -11
- package/src/slug.ts +0 -21
- package/src/types.ts +0 -49
- package/tsconfig.json +0 -7
- package/tsup.config.ts +0 -59
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { f as SnippetClient, e as SnippetClientOptions, a as Snippet, S as SnippetMeta } from '../snippet-client-CiQX2Zcn.js';
|
|
4
|
+
|
|
5
|
+
declare const SNIPPET_CLIENT: InjectionToken<SnippetClient>;
|
|
6
|
+
declare const SNIPPET_CLIENT_OPTIONS: InjectionToken<SnippetClientOptions>;
|
|
7
|
+
declare function provideSnippetClient(options: SnippetClientOptions): Provider[];
|
|
8
|
+
declare class MarkdownSnippetService {
|
|
9
|
+
private readonly client;
|
|
10
|
+
constructor(client: SnippetClient);
|
|
11
|
+
get(slug: string): Observable<Snippet>;
|
|
12
|
+
listAll(): Observable<SnippetMeta[]>;
|
|
13
|
+
listByGroup(group: string): Observable<SnippetMeta[]>;
|
|
14
|
+
listByType(type: string): Observable<SnippetMeta[]>;
|
|
15
|
+
html(slug: string): Observable<string>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { MarkdownSnippetService, SNIPPET_CLIENT, SNIPPET_CLIENT_OPTIONS, Snippet, SnippetClientOptions, SnippetMeta, provideSnippetClient };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SnippetClient
|
|
3
|
+
} from "../chunk-35YHML5Z.js";
|
|
4
|
+
import "../chunk-GWLMADTU.js";
|
|
5
|
+
import "../chunk-MWZFQXNW.js";
|
|
6
|
+
import {
|
|
7
|
+
__decorateClass,
|
|
8
|
+
__decorateParam
|
|
9
|
+
} from "../chunk-BRKEJJFQ.js";
|
|
10
|
+
|
|
11
|
+
// src/angular/index.ts
|
|
12
|
+
import { Inject, Injectable, InjectionToken } from "@angular/core";
|
|
13
|
+
import { from, map, shareReplay } from "rxjs";
|
|
14
|
+
var SNIPPET_CLIENT = new InjectionToken("@mzebley/mark-down/SNIPPET_CLIENT");
|
|
15
|
+
var SNIPPET_CLIENT_OPTIONS = new InjectionToken(
|
|
16
|
+
"@mzebley/mark-down/SNIPPET_CLIENT_OPTIONS"
|
|
17
|
+
);
|
|
18
|
+
function provideSnippetClient(options) {
|
|
19
|
+
return [
|
|
20
|
+
{ provide: SNIPPET_CLIENT_OPTIONS, useValue: options },
|
|
21
|
+
{
|
|
22
|
+
provide: SNIPPET_CLIENT,
|
|
23
|
+
useFactory: (opts) => new SnippetClient(opts),
|
|
24
|
+
deps: [SNIPPET_CLIENT_OPTIONS]
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
var MarkdownSnippetService = class {
|
|
29
|
+
constructor(client) {
|
|
30
|
+
this.client = client;
|
|
31
|
+
}
|
|
32
|
+
get(slug) {
|
|
33
|
+
return from(this.client.get(slug)).pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
|
34
|
+
}
|
|
35
|
+
listAll() {
|
|
36
|
+
return from(this.client.listAll()).pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
|
37
|
+
}
|
|
38
|
+
listByGroup(group) {
|
|
39
|
+
return from(this.client.listByGroup(group)).pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
|
40
|
+
}
|
|
41
|
+
listByType(type) {
|
|
42
|
+
return from(this.client.listByType(type)).pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
|
43
|
+
}
|
|
44
|
+
html(slug) {
|
|
45
|
+
return this.get(slug).pipe(map((snippet) => snippet.html));
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
MarkdownSnippetService = __decorateClass([
|
|
49
|
+
Injectable({ providedIn: "root" }),
|
|
50
|
+
__decorateParam(0, Inject(SNIPPET_CLIENT))
|
|
51
|
+
], MarkdownSnippetService);
|
|
52
|
+
export {
|
|
53
|
+
MarkdownSnippetService,
|
|
54
|
+
SNIPPET_CLIENT,
|
|
55
|
+
SNIPPET_CLIENT_OPTIONS,
|
|
56
|
+
provideSnippetClient
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/angular/index.ts"],"sourcesContent":["import { Inject, Injectable, InjectionToken, Provider } from \"@angular/core\";\nimport { from, map, Observable, shareReplay } from \"rxjs\";\nimport { SnippetClient } from \"../snippet-client\";\nimport type { Snippet, SnippetClientOptions, SnippetMeta } from \"../types\";\n\nexport const SNIPPET_CLIENT = new InjectionToken<SnippetClient>(\"@mzebley/mark-down/SNIPPET_CLIENT\");\nexport const SNIPPET_CLIENT_OPTIONS = new InjectionToken<SnippetClientOptions>(\n \"@mzebley/mark-down/SNIPPET_CLIENT_OPTIONS\"\n);\n\nexport function provideSnippetClient(options: SnippetClientOptions): Provider[] {\n return [\n { provide: SNIPPET_CLIENT_OPTIONS, useValue: options },\n {\n provide: SNIPPET_CLIENT,\n useFactory: (opts: SnippetClientOptions) => new SnippetClient(opts),\n deps: [SNIPPET_CLIENT_OPTIONS]\n }\n ];\n}\n\n@Injectable({ providedIn: \"root\" })\nexport class MarkdownSnippetService {\n constructor(@Inject(SNIPPET_CLIENT) private readonly client: SnippetClient) {}\n\n get(slug: string): Observable<Snippet> {\n return from(this.client.get(slug)).pipe(shareReplay({ bufferSize: 1, refCount: true }));\n }\n\n listAll(): Observable<SnippetMeta[]> {\n return from(this.client.listAll()).pipe(shareReplay({ bufferSize: 1, refCount: true }));\n }\n\n listByGroup(group: string): Observable<SnippetMeta[]> {\n return from(this.client.listByGroup(group)).pipe(shareReplay({ bufferSize: 1, refCount: true }));\n }\n\n listByType(type: string): Observable<SnippetMeta[]> {\n return from(this.client.listByType(type)).pipe(shareReplay({ bufferSize: 1, refCount: true }));\n }\n\n html(slug: string): Observable<string> {\n return this.get(slug).pipe(map((snippet) => snippet.html));\n }\n}\n\nexport type { Snippet, SnippetClientOptions, SnippetMeta } from \"../types\";\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,QAAQ,YAAY,sBAAgC;AAC7D,SAAS,MAAM,KAAiB,mBAAmB;AAI5C,IAAM,iBAAiB,IAAI,eAA8B,mCAAmC;AAC5F,IAAM,yBAAyB,IAAI;AAAA,EACxC;AACF;AAEO,SAAS,qBAAqB,SAA2C;AAC9E,SAAO;AAAA,IACL,EAAE,SAAS,wBAAwB,UAAU,QAAQ;AAAA,IACrD;AAAA,MACE,SAAS;AAAA,MACT,YAAY,CAAC,SAA+B,IAAI,cAAc,IAAI;AAAA,MAClE,MAAM,CAAC,sBAAsB;AAAA,IAC/B;AAAA,EACF;AACF;AAGO,IAAM,yBAAN,MAA6B;AAAA,EAClC,YAAqD,QAAuB;AAAvB;AAAA,EAAwB;AAAA,EAE7E,IAAI,MAAmC;AACrC,WAAO,KAAK,KAAK,OAAO,IAAI,IAAI,CAAC,EAAE,KAAK,YAAY,EAAE,YAAY,GAAG,UAAU,KAAK,CAAC,CAAC;AAAA,EACxF;AAAA,EAEA,UAAqC;AACnC,WAAO,KAAK,KAAK,OAAO,QAAQ,CAAC,EAAE,KAAK,YAAY,EAAE,YAAY,GAAG,UAAU,KAAK,CAAC,CAAC;AAAA,EACxF;AAAA,EAEA,YAAY,OAA0C;AACpD,WAAO,KAAK,KAAK,OAAO,YAAY,KAAK,CAAC,EAAE,KAAK,YAAY,EAAE,YAAY,GAAG,UAAU,KAAK,CAAC,CAAC;AAAA,EACjG;AAAA,EAEA,WAAW,MAAyC;AAClD,WAAO,KAAK,KAAK,OAAO,WAAW,IAAI,CAAC,EAAE,KAAK,YAAY,EAAE,YAAY,GAAG,UAAU,KAAK,CAAC,CAAC;AAAA,EAC/F;AAAA,EAEA,KAAK,MAAkC;AACrC,WAAO,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC,YAAY,QAAQ,IAAI,CAAC;AAAA,EAC3D;AACF;AAtBa,yBAAN;AAAA,EADN,WAAW,EAAE,YAAY,OAAO,CAAC;AAAA,EAEnB,0BAAO,cAAc;AAAA,GADvB;","names":[]}
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
// src/slug.ts
|
|
2
|
+
var NON_ALPHANUMERIC = /[^a-z0-9]+/gi;
|
|
3
|
+
var LEADING_TRAILING_DASH = /^-+|-+$/g;
|
|
4
|
+
function normalizeSlug(input) {
|
|
5
|
+
const value = input?.trim();
|
|
6
|
+
if (!value) {
|
|
7
|
+
throw new Error("Cannot normalize an empty slug");
|
|
8
|
+
}
|
|
9
|
+
const normalized = value.toLowerCase().replace(NON_ALPHANUMERIC, "-").replace(/-{2,}/g, "-").replace(LEADING_TRAILING_DASH, "");
|
|
10
|
+
if (!normalized) {
|
|
11
|
+
throw new Error(`Slug '${input}' does not contain any alphanumeric characters`);
|
|
12
|
+
}
|
|
13
|
+
return normalized;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/front-matter.ts
|
|
17
|
+
import { parse as parseYaml } from "yaml";
|
|
18
|
+
|
|
19
|
+
// src/errors.ts
|
|
20
|
+
var SnippetNotFoundError = class extends Error {
|
|
21
|
+
constructor(slug) {
|
|
22
|
+
super(`Snippet with slug '${slug}' was not found in the manifest.`);
|
|
23
|
+
this.name = "SnippetNotFoundError";
|
|
24
|
+
this.slug = slug;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var ManifestLoadError = class extends Error {
|
|
28
|
+
constructor(message, cause) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "ManifestLoadError";
|
|
31
|
+
this.cause = cause instanceof Error ? cause : cause ? new Error(String(cause)) : void 0;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/front-matter.ts
|
|
36
|
+
var FRONT_MATTER_PATTERN = /^(?:\uFEFF)?[ \t\r\n]*---\s*\r?\n([\s\S]*?)\r?\n---\s*\r?\n?/;
|
|
37
|
+
function parseFrontMatter(raw) {
|
|
38
|
+
const match = FRONT_MATTER_PATTERN.exec(raw);
|
|
39
|
+
if (!match) {
|
|
40
|
+
return { content: raw, meta: {}, extra: {}, hasFrontMatter: false };
|
|
41
|
+
}
|
|
42
|
+
const yamlSection = match[1];
|
|
43
|
+
let data;
|
|
44
|
+
try {
|
|
45
|
+
data = parseYaml(yamlSection) ?? {};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw new ManifestLoadError("Failed to parse snippet front-matter.", error);
|
|
48
|
+
}
|
|
49
|
+
if (!isRecord(data)) {
|
|
50
|
+
return { content: raw.slice(match[0].length), meta: {}, extra: {}, hasFrontMatter: true };
|
|
51
|
+
}
|
|
52
|
+
const { known, extra } = splitFrontMatter(data);
|
|
53
|
+
return {
|
|
54
|
+
content: raw.slice(match[0].length),
|
|
55
|
+
meta: known.meta,
|
|
56
|
+
extra,
|
|
57
|
+
slug: known.slug,
|
|
58
|
+
hasFrontMatter: true
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function splitFrontMatter(data) {
|
|
62
|
+
const meta = {};
|
|
63
|
+
const extra = {};
|
|
64
|
+
let slug;
|
|
65
|
+
for (const [key, value] of Object.entries(data)) {
|
|
66
|
+
switch (key) {
|
|
67
|
+
case "slug":
|
|
68
|
+
slug = typeof value === "string" ? value : void 0;
|
|
69
|
+
break;
|
|
70
|
+
case "title":
|
|
71
|
+
if (typeof value === "string") {
|
|
72
|
+
meta.title = value;
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
case "type":
|
|
76
|
+
if (typeof value === "string") {
|
|
77
|
+
meta.type = value;
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case "order":
|
|
81
|
+
if (typeof value === "number") {
|
|
82
|
+
meta.order = value;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case "tags":
|
|
86
|
+
meta.tags = normalizeTags(value);
|
|
87
|
+
break;
|
|
88
|
+
case "group":
|
|
89
|
+
if (typeof value === "string" || value === null) {
|
|
90
|
+
meta.group = value;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
case "draft":
|
|
94
|
+
if (typeof value === "boolean") {
|
|
95
|
+
meta.draft = value;
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
extra[key] = value;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { known: { meta, slug }, extra };
|
|
104
|
+
}
|
|
105
|
+
function normalizeTags(value) {
|
|
106
|
+
if (!value) {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(value)) {
|
|
110
|
+
return value.map((item) => String(item));
|
|
111
|
+
}
|
|
112
|
+
if (typeof value === "string") {
|
|
113
|
+
return value.split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
114
|
+
}
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
function isRecord(candidate) {
|
|
118
|
+
return Boolean(candidate) && typeof candidate === "object" && !Array.isArray(candidate);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/markdown.ts
|
|
122
|
+
import { marked } from "marked";
|
|
123
|
+
function renderMarkdown(markdown) {
|
|
124
|
+
const html = marked.parse(markdown);
|
|
125
|
+
if (typeof html === "string") {
|
|
126
|
+
return html;
|
|
127
|
+
}
|
|
128
|
+
throw new Error("renderMarkdown unexpectedly returned a Promise");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/snippet-client.ts
|
|
132
|
+
var HTTP_PATTERN = /^https?:\/\//i;
|
|
133
|
+
var SnippetClient = class {
|
|
134
|
+
constructor(options) {
|
|
135
|
+
this.snippetCache = /* @__PURE__ */ new Map();
|
|
136
|
+
if (!options || !options.manifest) {
|
|
137
|
+
throw new ManifestLoadError("A manifest source must be provided to SnippetClient.");
|
|
138
|
+
}
|
|
139
|
+
this.manifestUrl = typeof options.manifest === "string" ? options.manifest : void 0;
|
|
140
|
+
this.inferredBase = this.manifestUrl ? deriveBaseFromManifest(this.manifestUrl) : void 0;
|
|
141
|
+
const fetcher = options.fetch ?? defaultFetch;
|
|
142
|
+
const renderOption = options.render;
|
|
143
|
+
const renderer = renderOption ? async (markdown) => Promise.resolve(renderOption(markdown)) : async (markdown) => Promise.resolve(renderMarkdown(markdown));
|
|
144
|
+
this.options = {
|
|
145
|
+
manifest: options.manifest,
|
|
146
|
+
base: options.base,
|
|
147
|
+
fetch: async (url) => {
|
|
148
|
+
const response = await fetcher(url);
|
|
149
|
+
if (typeof response === "string") {
|
|
150
|
+
return response;
|
|
151
|
+
}
|
|
152
|
+
return resolveResponseText(response, url);
|
|
153
|
+
},
|
|
154
|
+
frontMatter: options.frontMatter !== false,
|
|
155
|
+
cache: options.cache !== false,
|
|
156
|
+
verbose: options.verbose === true,
|
|
157
|
+
render: renderer
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async get(slug) {
|
|
161
|
+
const manifest = await this.loadManifest();
|
|
162
|
+
const entry = manifest.find((item) => item.slug === slug);
|
|
163
|
+
if (!entry) {
|
|
164
|
+
throw new SnippetNotFoundError(slug);
|
|
165
|
+
}
|
|
166
|
+
return this.loadSnippet(entry);
|
|
167
|
+
}
|
|
168
|
+
async listAll() {
|
|
169
|
+
const manifest = await this.loadManifest();
|
|
170
|
+
return manifest.map(cloneMeta);
|
|
171
|
+
}
|
|
172
|
+
async listByGroup(group) {
|
|
173
|
+
const manifest = await this.loadManifest();
|
|
174
|
+
return manifest.filter((item) => (item.group ?? null) === group).map(cloneMeta);
|
|
175
|
+
}
|
|
176
|
+
async listByType(type) {
|
|
177
|
+
const manifest = await this.loadManifest();
|
|
178
|
+
return manifest.filter((item) => item.type === type).map(cloneMeta);
|
|
179
|
+
}
|
|
180
|
+
async search(filter) {
|
|
181
|
+
const manifest = await this.loadManifest();
|
|
182
|
+
const tags = filter.tags ?? [];
|
|
183
|
+
const mode = filter.tagsMode ?? "any";
|
|
184
|
+
return manifest.filter((item) => {
|
|
185
|
+
if (filter.type && item.type !== filter.type) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
if (filter.group && (item.group ?? void 0) !== filter.group) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
if (!tags.length) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
const metaTags = item.tags ?? [];
|
|
195
|
+
if (!metaTags.length) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
if (mode === "all") {
|
|
199
|
+
return tags.every((tag) => metaTags.includes(tag));
|
|
200
|
+
}
|
|
201
|
+
return tags.some((tag) => metaTags.includes(tag));
|
|
202
|
+
}).map(cloneMeta);
|
|
203
|
+
}
|
|
204
|
+
async getHtml(slug) {
|
|
205
|
+
const snippet = await this.get(slug);
|
|
206
|
+
return snippet.html;
|
|
207
|
+
}
|
|
208
|
+
invalidate() {
|
|
209
|
+
this.manifestPromise = void 0;
|
|
210
|
+
this.snippetCache.clear();
|
|
211
|
+
}
|
|
212
|
+
invalidateSlug(slug) {
|
|
213
|
+
this.snippetCache.delete(slug);
|
|
214
|
+
}
|
|
215
|
+
async loadManifest() {
|
|
216
|
+
if (this.options.cache && this.manifestPromise) {
|
|
217
|
+
return this.manifestPromise;
|
|
218
|
+
}
|
|
219
|
+
const promise = this.resolveManifest();
|
|
220
|
+
if (this.options.cache) {
|
|
221
|
+
this.manifestPromise = promise;
|
|
222
|
+
}
|
|
223
|
+
return promise;
|
|
224
|
+
}
|
|
225
|
+
async resolveManifest() {
|
|
226
|
+
const source = this.options.manifest;
|
|
227
|
+
let entries;
|
|
228
|
+
try {
|
|
229
|
+
if (Array.isArray(source)) {
|
|
230
|
+
entries = source.map(normalizeManifestEntry);
|
|
231
|
+
} else if (typeof source === "function") {
|
|
232
|
+
const result = await source();
|
|
233
|
+
if (!Array.isArray(result)) {
|
|
234
|
+
throw new ManifestLoadError("Manifest loader must resolve to an array of snippet metadata.");
|
|
235
|
+
}
|
|
236
|
+
entries = result.map(normalizeManifestEntry);
|
|
237
|
+
} else {
|
|
238
|
+
const raw = await this.options.fetch(source);
|
|
239
|
+
entries = parseManifest(raw, source).map(normalizeManifestEntry);
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
if (error instanceof ManifestLoadError) {
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
throw new ManifestLoadError("Failed to load snippet manifest.", error);
|
|
246
|
+
}
|
|
247
|
+
return entries.map(cloneMeta);
|
|
248
|
+
}
|
|
249
|
+
loadSnippet(meta) {
|
|
250
|
+
const cached = this.options.cache ? this.snippetCache.get(meta.slug) : void 0;
|
|
251
|
+
if (cached) {
|
|
252
|
+
return cached;
|
|
253
|
+
}
|
|
254
|
+
const promise = this.fetchSnippet(meta);
|
|
255
|
+
if (this.options.cache) {
|
|
256
|
+
this.snippetCache.set(meta.slug, promise);
|
|
257
|
+
}
|
|
258
|
+
return promise;
|
|
259
|
+
}
|
|
260
|
+
async fetchSnippet(meta) {
|
|
261
|
+
const url = this.resolveSnippetPath(meta.path);
|
|
262
|
+
let raw;
|
|
263
|
+
try {
|
|
264
|
+
raw = await this.options.fetch(url);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
throw new ManifestLoadError(`Failed to fetch snippet at '${url}'.`, error);
|
|
267
|
+
}
|
|
268
|
+
const frontMatter = this.options.frontMatter ? parseFrontMatter(raw) : void 0;
|
|
269
|
+
const body = frontMatter?.content ?? raw;
|
|
270
|
+
const html = await this.options.render(body);
|
|
271
|
+
const merged = {
|
|
272
|
+
...meta,
|
|
273
|
+
...pickMeta(frontMatter?.meta),
|
|
274
|
+
extra: mergeExtra(meta.extra, frontMatter?.extra)
|
|
275
|
+
};
|
|
276
|
+
if (frontMatter?.slug) {
|
|
277
|
+
try {
|
|
278
|
+
const normalizedFrontSlug = normalizeSlug(frontMatter.slug);
|
|
279
|
+
if (normalizedFrontSlug !== meta.slug && this.options.verbose) {
|
|
280
|
+
console.warn(
|
|
281
|
+
`Front-matter slug '${frontMatter.slug}' (normalized: '${normalizedFrontSlug}') differs from manifest slug '${meta.slug}'.`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (this.options.verbose) {
|
|
286
|
+
console.warn(`Failed to normalize front-matter slug '${frontMatter.slug}':`, error);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const snippet = { ...merged, html, raw: body, markdown: body };
|
|
291
|
+
if (merged.tags) {
|
|
292
|
+
snippet.tags = [...merged.tags];
|
|
293
|
+
}
|
|
294
|
+
return snippet;
|
|
295
|
+
}
|
|
296
|
+
resolveSnippetPath(path) {
|
|
297
|
+
if (HTTP_PATTERN.test(path)) {
|
|
298
|
+
return normalizeForwardSlashes(path);
|
|
299
|
+
}
|
|
300
|
+
const base = this.options.base ?? this.inferredBase ?? "";
|
|
301
|
+
if (path.startsWith("/")) {
|
|
302
|
+
if (base) {
|
|
303
|
+
return joinPaths(base, path);
|
|
304
|
+
}
|
|
305
|
+
return normalizeForwardSlashes(path);
|
|
306
|
+
}
|
|
307
|
+
if (base) {
|
|
308
|
+
return joinPaths(base, path);
|
|
309
|
+
}
|
|
310
|
+
return normalizeForwardSlashes(path);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
function parseManifest(raw, source) {
|
|
314
|
+
let parsed;
|
|
315
|
+
try {
|
|
316
|
+
parsed = JSON.parse(raw);
|
|
317
|
+
} catch (error) {
|
|
318
|
+
throw new ManifestLoadError(`Manifest at '${source}' is not valid JSON.`, error);
|
|
319
|
+
}
|
|
320
|
+
if (!Array.isArray(parsed)) {
|
|
321
|
+
throw new ManifestLoadError(`Manifest at '${source}' must be a JSON array.`);
|
|
322
|
+
}
|
|
323
|
+
return parsed;
|
|
324
|
+
}
|
|
325
|
+
function normalizeManifestEntry(entry) {
|
|
326
|
+
if (!entry.slug) {
|
|
327
|
+
throw new ManifestLoadError("Manifest entry is missing required 'slug' property.");
|
|
328
|
+
}
|
|
329
|
+
if (!entry.path) {
|
|
330
|
+
throw new ManifestLoadError(`Manifest entry for '${entry.slug}' is missing required 'path'.`);
|
|
331
|
+
}
|
|
332
|
+
const normalized = {
|
|
333
|
+
slug: entry.slug,
|
|
334
|
+
title: entry.title,
|
|
335
|
+
type: entry.type,
|
|
336
|
+
order: entry.order,
|
|
337
|
+
tags: entry.tags ? [...entry.tags] : void 0,
|
|
338
|
+
path: normalizeForwardSlashes(entry.path),
|
|
339
|
+
group: entry.group ?? null,
|
|
340
|
+
draft: entry.draft,
|
|
341
|
+
extra: cloneRecord(entry.extra)
|
|
342
|
+
};
|
|
343
|
+
return normalized;
|
|
344
|
+
}
|
|
345
|
+
function deriveBaseFromManifest(manifest) {
|
|
346
|
+
if (HTTP_PATTERN.test(manifest)) {
|
|
347
|
+
try {
|
|
348
|
+
const url = new URL(manifest);
|
|
349
|
+
url.hash = "";
|
|
350
|
+
url.search = "";
|
|
351
|
+
const path = url.pathname;
|
|
352
|
+
url.pathname = path.replace(/[^/]*$/, "");
|
|
353
|
+
return url.toString();
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.warn(`Unable to derive base from manifest '${manifest}':`, error);
|
|
356
|
+
return void 0;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const sanitized = manifest.replace(/[?#].*$/, "");
|
|
360
|
+
const index = sanitized.lastIndexOf("/");
|
|
361
|
+
if (index === -1) {
|
|
362
|
+
return void 0;
|
|
363
|
+
}
|
|
364
|
+
return sanitized.slice(0, index + 1);
|
|
365
|
+
}
|
|
366
|
+
function joinPaths(base, relative) {
|
|
367
|
+
if (HTTP_PATTERN.test(base)) {
|
|
368
|
+
return new URL(relative, base).toString();
|
|
369
|
+
}
|
|
370
|
+
const leading = base.endsWith("/") ? base : `${base}/`;
|
|
371
|
+
const trimmed = relative.startsWith("/") ? relative.slice(1) : relative;
|
|
372
|
+
return normalizeForwardSlashes(`${leading}${trimmed}`);
|
|
373
|
+
}
|
|
374
|
+
function normalizeForwardSlashes(value) {
|
|
375
|
+
if (HTTP_PATTERN.test(value)) {
|
|
376
|
+
try {
|
|
377
|
+
const url = new URL(value);
|
|
378
|
+
url.pathname = url.pathname.replace(/\/{2,}/g, "/");
|
|
379
|
+
return url.toString();
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (value.startsWith("//")) {
|
|
384
|
+
return `//${value.slice(2).replace(/\/{2,}/g, "/")}`;
|
|
385
|
+
}
|
|
386
|
+
if (value.startsWith("/")) {
|
|
387
|
+
return `/${value.slice(1).replace(/\/{2,}/g, "/")}`;
|
|
388
|
+
}
|
|
389
|
+
return value.replace(/\/{2,}/g, "/");
|
|
390
|
+
}
|
|
391
|
+
function mergeExtra(base, overrides) {
|
|
392
|
+
if (!base && !overrides) {
|
|
393
|
+
return void 0;
|
|
394
|
+
}
|
|
395
|
+
return { ...base ?? {}, ...overrides ?? {} };
|
|
396
|
+
}
|
|
397
|
+
function cloneRecord(value) {
|
|
398
|
+
if (!value) {
|
|
399
|
+
return value;
|
|
400
|
+
}
|
|
401
|
+
return { ...value };
|
|
402
|
+
}
|
|
403
|
+
function cloneMeta(meta) {
|
|
404
|
+
return {
|
|
405
|
+
...meta,
|
|
406
|
+
tags: meta.tags ? [...meta.tags] : void 0,
|
|
407
|
+
extra: cloneRecord(meta.extra)
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
async function defaultFetch(url) {
|
|
411
|
+
const runtimeFetch = globalThis.fetch;
|
|
412
|
+
if (!runtimeFetch) {
|
|
413
|
+
throw new ManifestLoadError("No global fetch implementation is available. Provide a custom fetch function.");
|
|
414
|
+
}
|
|
415
|
+
return runtimeFetch(url);
|
|
416
|
+
}
|
|
417
|
+
async function resolveResponseText(response, url) {
|
|
418
|
+
if (!response.ok) {
|
|
419
|
+
throw new ManifestLoadError(`Request to '${url}' failed with status ${response.status}.`);
|
|
420
|
+
}
|
|
421
|
+
return response.text();
|
|
422
|
+
}
|
|
423
|
+
function pickMeta(meta) {
|
|
424
|
+
if (!meta) {
|
|
425
|
+
return {};
|
|
426
|
+
}
|
|
427
|
+
const result = {};
|
|
428
|
+
if (meta.title !== void 0) {
|
|
429
|
+
result.title = meta.title;
|
|
430
|
+
}
|
|
431
|
+
if (meta.type !== void 0) {
|
|
432
|
+
result.type = meta.type;
|
|
433
|
+
}
|
|
434
|
+
if (meta.order !== void 0) {
|
|
435
|
+
result.order = meta.order;
|
|
436
|
+
}
|
|
437
|
+
if (meta.tags !== void 0) {
|
|
438
|
+
result.tags = [...meta.tags];
|
|
439
|
+
}
|
|
440
|
+
if (meta.group !== void 0) {
|
|
441
|
+
result.group = meta.group;
|
|
442
|
+
}
|
|
443
|
+
if (meta.draft !== void 0) {
|
|
444
|
+
result.draft = meta.draft;
|
|
445
|
+
}
|
|
446
|
+
if (meta.path !== void 0) {
|
|
447
|
+
result.path = meta.path;
|
|
448
|
+
}
|
|
449
|
+
return result;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/browser.ts
|
|
453
|
+
installBufferShim();
|
|
454
|
+
function installBufferShim() {
|
|
455
|
+
const globalRef = globalThis;
|
|
456
|
+
if (typeof globalRef.Buffer !== "undefined") {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const textEncoder = new TextEncoder();
|
|
460
|
+
const textDecoder = new TextDecoder();
|
|
461
|
+
class BrowserBuffer extends Uint8Array {
|
|
462
|
+
constructor(value) {
|
|
463
|
+
if (typeof value === "number") {
|
|
464
|
+
super(value);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (isArrayBufferLike(value)) {
|
|
468
|
+
super(new Uint8Array(value));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (ArrayBuffer.isView(value)) {
|
|
472
|
+
super(
|
|
473
|
+
new Uint8Array(value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength))
|
|
474
|
+
);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
super(Array.from(value));
|
|
478
|
+
}
|
|
479
|
+
toString(encoding = "utf-8") {
|
|
480
|
+
if (encoding !== "utf-8" && encoding !== "utf8") {
|
|
481
|
+
throw new Error(`Unsupported encoding '${encoding}' in browser Buffer shim`);
|
|
482
|
+
}
|
|
483
|
+
return textDecoder.decode(this);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const from = (value, encoding = "utf-8") => {
|
|
487
|
+
if (typeof value === "string") {
|
|
488
|
+
if (encoding !== "utf-8" && encoding !== "utf8") {
|
|
489
|
+
throw new Error(`Unsupported encoding '${encoding}' in browser Buffer shim`);
|
|
490
|
+
}
|
|
491
|
+
return new BrowserBuffer(textEncoder.encode(value));
|
|
492
|
+
}
|
|
493
|
+
if (isArrayBufferLike(value)) {
|
|
494
|
+
return new BrowserBuffer(new Uint8Array(value));
|
|
495
|
+
}
|
|
496
|
+
if (ArrayBuffer.isView(value)) {
|
|
497
|
+
return new BrowserBuffer(value);
|
|
498
|
+
}
|
|
499
|
+
if (typeof value.length === "number") {
|
|
500
|
+
return new BrowserBuffer(Array.from(value));
|
|
501
|
+
}
|
|
502
|
+
throw new TypeError("Unsupported input passed to Buffer.from in browser shim");
|
|
503
|
+
};
|
|
504
|
+
const alloc = (size, fill) => {
|
|
505
|
+
if (size < 0) {
|
|
506
|
+
throw new RangeError("Invalid Buffer size");
|
|
507
|
+
}
|
|
508
|
+
const buffer = new BrowserBuffer(size);
|
|
509
|
+
if (typeof fill === "number") {
|
|
510
|
+
buffer.fill(fill);
|
|
511
|
+
} else if (typeof fill === "string") {
|
|
512
|
+
if (!fill.length) {
|
|
513
|
+
buffer.fill(0);
|
|
514
|
+
} else {
|
|
515
|
+
const pattern = textEncoder.encode(fill);
|
|
516
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
517
|
+
buffer[i] = pattern[i % pattern.length];
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
buffer.fill(0);
|
|
522
|
+
}
|
|
523
|
+
return buffer;
|
|
524
|
+
};
|
|
525
|
+
const concat = (buffers, totalLength) => {
|
|
526
|
+
const sanitized = Array.from(
|
|
527
|
+
buffers,
|
|
528
|
+
(buffer) => buffer instanceof BrowserBuffer ? buffer : new BrowserBuffer(buffer)
|
|
529
|
+
);
|
|
530
|
+
const length = totalLength ?? sanitized.reduce((acc, current) => acc + current.length, 0);
|
|
531
|
+
const result = new BrowserBuffer(length);
|
|
532
|
+
let offset = 0;
|
|
533
|
+
for (const buffer of sanitized) {
|
|
534
|
+
result.set(buffer, offset);
|
|
535
|
+
offset += buffer.length;
|
|
536
|
+
}
|
|
537
|
+
return result;
|
|
538
|
+
};
|
|
539
|
+
const byteLength = (value) => {
|
|
540
|
+
if (typeof value === "string") {
|
|
541
|
+
return textEncoder.encode(value).length;
|
|
542
|
+
}
|
|
543
|
+
if (value instanceof ArrayBuffer) {
|
|
544
|
+
return value.byteLength;
|
|
545
|
+
}
|
|
546
|
+
if (ArrayBuffer.isView(value)) {
|
|
547
|
+
return value.byteLength;
|
|
548
|
+
}
|
|
549
|
+
throw new TypeError("Unable to determine byte length for provided value");
|
|
550
|
+
};
|
|
551
|
+
Object.defineProperties(BrowserBuffer, {
|
|
552
|
+
from: { value: from },
|
|
553
|
+
isBuffer: { value: (candidate) => candidate instanceof BrowserBuffer },
|
|
554
|
+
alloc: { value: alloc },
|
|
555
|
+
concat: { value: concat },
|
|
556
|
+
byteLength: { value: byteLength }
|
|
557
|
+
});
|
|
558
|
+
BrowserBuffer.prototype.valueOf = function valueOf() {
|
|
559
|
+
return this;
|
|
560
|
+
};
|
|
561
|
+
globalRef.Buffer = BrowserBuffer;
|
|
562
|
+
if (typeof window !== "undefined" && typeof window.Buffer === "undefined") {
|
|
563
|
+
window.Buffer = globalRef.Buffer;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function isArrayBufferLike(value) {
|
|
567
|
+
if (value instanceof ArrayBuffer) {
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
return typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer;
|
|
571
|
+
}
|
|
572
|
+
export {
|
|
573
|
+
ManifestLoadError,
|
|
574
|
+
SnippetClient,
|
|
575
|
+
SnippetNotFoundError,
|
|
576
|
+
normalizeSlug,
|
|
577
|
+
parseFrontMatter,
|
|
578
|
+
renderMarkdown
|
|
579
|
+
};
|
|
580
|
+
//# sourceMappingURL=browser.js.map
|