emdash-emit 0.1.16 → 0.1.18
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/CHANGELOG.md +55 -0
- package/README.md +1 -1
- package/dist/cli/commands/new.d.ts.map +1 -1
- package/dist/cli/commands/new.js +1 -13
- package/dist/cli/commands/new.js.map +1 -1
- package/dist/cli/commands/pull.d.ts +2 -10
- package/dist/cli/commands/pull.d.ts.map +1 -1
- package/dist/cli/commands/pull.js +12 -30
- package/dist/cli/commands/pull.js.map +1 -1
- package/dist/client/auth.d.ts.map +1 -1
- package/dist/client/auth.js +2 -1
- package/dist/client/auth.js.map +1 -1
- package/dist/client/endpoints.d.ts +19 -0
- package/dist/client/endpoints.d.ts.map +1 -0
- package/dist/client/endpoints.js +69 -0
- package/dist/client/endpoints.js.map +1 -0
- package/dist/client/http.d.ts +4 -2
- package/dist/client/http.d.ts.map +1 -1
- package/dist/client/http.js.map +1 -1
- package/dist/client/lock.d.ts.map +1 -1
- package/dist/client/lock.js +4 -3
- package/dist/client/lock.js.map +1 -1
- package/dist/client/mapper.d.ts.map +1 -1
- package/dist/client/mapper.js +5 -3
- package/dist/client/mapper.js.map +1 -1
- package/dist/client/media-id.d.ts +2 -0
- package/dist/client/media-id.d.ts.map +1 -0
- package/dist/client/media-id.js +23 -0
- package/dist/client/media-id.js.map +1 -0
- package/dist/client/media.d.ts +8 -7
- package/dist/client/media.d.ts.map +1 -1
- package/dist/client/media.js +58 -17
- package/dist/client/media.js.map +1 -1
- package/dist/db/baseline.d.ts +37 -0
- package/dist/db/baseline.d.ts.map +1 -0
- package/dist/db/baseline.js +74 -0
- package/dist/db/baseline.js.map +1 -0
- package/dist/db/cache.d.ts +3 -2
- package/dist/db/cache.d.ts.map +1 -1
- package/dist/db/cache.js +3 -2
- package/dist/db/cache.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools/sync-new.d.ts.map +1 -1
- package/dist/mcp/tools/sync-new.js +1 -13
- package/dist/mcp/tools/sync-new.js.map +1 -1
- package/dist/mcp/tools/sync-pull.d.ts.map +1 -1
- package/dist/mcp/tools/sync-pull.js +15 -2
- package/dist/mcp/tools/sync-pull.js.map +1 -1
- package/dist/presentation/article_report.d.ts +40 -0
- package/dist/presentation/article_report.d.ts.map +1 -0
- package/dist/presentation/article_report.js +80 -0
- package/dist/presentation/article_report.js.map +1 -0
- package/dist/sync/bylines.d.ts.map +1 -1
- package/dist/sync/bylines.js +2 -1
- package/dist/sync/bylines.js.map +1 -1
- package/dist/sync/conflicts.js +1 -1
- package/dist/sync/conflicts.js.map +1 -1
- package/dist/sync/delete.d.ts +0 -5
- package/dist/sync/delete.d.ts.map +1 -1
- package/dist/sync/delete.js +5 -11
- package/dist/sync/delete.js.map +1 -1
- package/dist/sync/diff.d.ts.map +1 -1
- package/dist/sync/diff.js +4 -5
- package/dist/sync/diff.js.map +1 -1
- package/dist/sync/doctor.d.ts.map +1 -1
- package/dist/sync/doctor.js +11 -14
- package/dist/sync/doctor.js.map +1 -1
- package/dist/sync/guards.d.ts +5 -0
- package/dist/sync/guards.d.ts.map +1 -1
- package/dist/sync/guards.js +61 -0
- package/dist/sync/guards.js.map +1 -1
- package/dist/sync/media/body_scan.d.ts.map +1 -1
- package/dist/sync/media/body_scan.js +9 -3
- package/dist/sync/media/body_scan.js.map +1 -1
- package/dist/sync/media/companions.d.ts +2 -9
- package/dist/sync/media/companions.d.ts.map +1 -1
- package/dist/sync/media/companions.js +5 -23
- package/dist/sync/media/companions.js.map +1 -1
- package/dist/sync/media/mirror_item.d.ts.map +1 -1
- package/dist/sync/media/mirror_item.js +33 -21
- package/dist/sync/media/mirror_item.js.map +1 -1
- package/dist/sync/media/pull_article.d.ts.map +1 -1
- package/dist/sync/media/pull_article.js +3 -2
- package/dist/sync/media/pull_article.js.map +1 -1
- package/dist/sync/media/pull_library.d.ts.map +1 -1
- package/dist/sync/media/pull_library.js +16 -11
- package/dist/sync/media/pull_library.js.map +1 -1
- package/dist/sync/media/remote_index.d.ts.map +1 -1
- package/dist/sync/media/remote_index.js +2 -1
- package/dist/sync/media/remote_index.js.map +1 -1
- package/dist/sync/media/types.d.ts +4 -3
- package/dist/sync/media/types.d.ts.map +1 -1
- package/dist/sync/media/types.js.map +1 -1
- package/dist/sync/new.d.ts +1 -12
- package/dist/sync/new.d.ts.map +1 -1
- package/dist/sync/new.js +0 -39
- package/dist/sync/new.js.map +1 -1
- package/dist/sync/preflight.d.ts +3 -2
- package/dist/sync/preflight.d.ts.map +1 -1
- package/dist/sync/preflight.js +19 -10
- package/dist/sync/preflight.js.map +1 -1
- package/dist/sync/pull.d.ts +6 -44
- package/dist/sync/pull.d.ts.map +1 -1
- package/dist/sync/pull.js +37 -145
- package/dist/sync/pull.js.map +1 -1
- package/dist/sync/push/commit.d.ts +60 -0
- package/dist/sync/push/commit.d.ts.map +1 -0
- package/dist/sync/push/commit.js +230 -0
- package/dist/sync/push/commit.js.map +1 -0
- package/dist/sync/push/guards.d.ts +33 -0
- package/dist/sync/push/guards.d.ts.map +1 -0
- package/dist/sync/push/guards.js +110 -0
- package/dist/sync/push/guards.js.map +1 -0
- package/dist/sync/push/index.d.ts +9 -0
- package/dist/sync/push/index.d.ts.map +1 -0
- package/dist/sync/push/index.js +350 -0
- package/dist/sync/push/index.js.map +1 -0
- package/dist/sync/push/media_stage.d.ts +41 -0
- package/dist/sync/push/media_stage.d.ts.map +1 -0
- package/dist/sync/push/media_stage.js +309 -0
- package/dist/sync/push/media_stage.js.map +1 -0
- package/dist/sync/push/merge_stage.d.ts +37 -0
- package/dist/sync/push/merge_stage.d.ts.map +1 -0
- package/dist/sync/push/merge_stage.js +243 -0
- package/dist/sync/push/merge_stage.js.map +1 -0
- package/dist/sync/push/preflight_stage.d.ts +6 -0
- package/dist/sync/push/preflight_stage.d.ts.map +1 -0
- package/dist/sync/push/preflight_stage.js +40 -0
- package/dist/sync/push/preflight_stage.js.map +1 -0
- package/dist/sync/push/tax_audit.d.ts +24 -0
- package/dist/sync/push/tax_audit.d.ts.map +1 -0
- package/dist/sync/push/tax_audit.js +70 -0
- package/dist/sync/push/tax_audit.js.map +1 -0
- package/dist/sync/push/taxonomy_plan.d.ts +36 -0
- package/dist/sync/push/taxonomy_plan.d.ts.map +1 -0
- package/dist/sync/push/taxonomy_plan.js +136 -0
- package/dist/sync/push/taxonomy_plan.js.map +1 -0
- package/dist/sync/push/transport.d.ts +10 -0
- package/dist/sync/push/transport.d.ts.map +1 -0
- package/dist/sync/push/transport.js +74 -0
- package/dist/sync/push/transport.js.map +1 -0
- package/dist/sync/push/types.d.ts +62 -0
- package/dist/sync/push/types.d.ts.map +1 -0
- package/dist/sync/push/types.js +23 -0
- package/dist/sync/push/types.js.map +1 -0
- package/dist/sync/push.d.ts +2 -57
- package/dist/sync/push.d.ts.map +1 -1
- package/dist/sync/push.js +2 -1137
- package/dist/sync/push.js.map +1 -1
- package/dist/sync/status.d.ts.map +1 -1
- package/dist/sync/status.js +5 -4
- package/dist/sync/status.js.map +1 -1
- package/dist/sync/taxonomies.d.ts.map +1 -1
- package/dist/sync/taxonomies.js +2 -1
- package/dist/sync/taxonomies.js.map +1 -1
- package/dist/sync/terms.d.ts.map +1 -1
- package/dist/sync/terms.js +2 -1
- package/dist/sync/terms.js.map +1 -1
- package/dist/sync/workspace/index.d.ts +41 -0
- package/dist/sync/workspace/index.d.ts.map +1 -0
- package/dist/sync/workspace/index.js +124 -0
- package/dist/sync/workspace/index.js.map +1 -0
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +1 -3
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/media-body-refs.d.ts +24 -0
- package/dist/utils/media-body-refs.d.ts.map +1 -1
- package/dist/utils/media-body-refs.js +51 -0
- package/dist/utils/media-body-refs.js.map +1 -1
- package/dist/utils/media-companion.d.ts +13 -0
- package/dist/utils/media-companion.d.ts.map +1 -0
- package/dist/utils/media-companion.js +42 -0
- package/dist/utils/media-companion.js.map +1 -0
- package/dist/utils/media-id.d.ts +46 -0
- package/dist/utils/media-id.d.ts.map +1 -0
- package/dist/utils/media-id.js +84 -0
- package/dist/utils/media-id.js.map +1 -0
- package/dist/utils/naming.d.ts.map +1 -1
- package/dist/utils/naming.js +3 -2
- package/dist/utils/naming.js.map +1 -1
- package/dist/utils/ulid.d.ts +31 -0
- package/dist/utils/ulid.d.ts.map +1 -0
- package/dist/utils/ulid.js +65 -0
- package/dist/utils/ulid.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FencePolicyType } from './media-refs.js';
|
|
1
2
|
/** Canonical root-relative media route prefix EmDash writes into Portable Text image blocks. */
|
|
2
3
|
export declare const MEDIA_FILE_ROUTE_PREFIX = "/_emdash/api/media/file/";
|
|
3
4
|
/** Workspace-relative directory a mirrored asset lives in. */
|
|
@@ -50,6 +51,16 @@ export declare function canonicalToLocalMediaRefs(body: string, options: {
|
|
|
50
51
|
* reaches the push guards instead of being silently rewritten.
|
|
51
52
|
*/
|
|
52
53
|
export declare function storageKeyFromMirrorRef(rawPath: string, candidateStorageKeys: Iterable<string>): string | null;
|
|
54
|
+
/**
|
|
55
|
+
* Scans a Markdown article body for every media reference it carries.
|
|
56
|
+
*
|
|
57
|
+
* This is the workspace-contract entry point Layer 2 consumers (delete, doctor, media) call instead of
|
|
58
|
+
* reaching into each other: markdown images and HTML `<img>` tags anywhere in the body, including inside
|
|
59
|
+
* opaque `<!--ec:block ... -->` fences and legacy container lines such as `[dimage url="..." control=true]`.
|
|
60
|
+
* External `http(s)` and `data:` URLs are excluded because they name no local mirror. The fence policy is
|
|
61
|
+
* delegated to `utils/media-refs.ts` so the guard/diagnostic distinction stays declared in exactly one place.
|
|
62
|
+
*/
|
|
63
|
+
export declare function scanArticleMediaReferences(bodyMarkdown: string, fencePolicy?: FencePolicyType): string[];
|
|
53
64
|
/**
|
|
54
65
|
* Builds the `../../` depth an article at `articleRelPath` needs to reach `media/` from its own directory,
|
|
55
66
|
* matching the prefix the draft-image guards already accept.
|
|
@@ -78,4 +89,17 @@ export declare function localizeArticleMediaRefs(body: string, options: {
|
|
|
78
89
|
/** Reproduce the depth-free `media/<storageKey>` spelling instead of preferring the article-relative one. */
|
|
79
90
|
preferLegacyRef?: boolean;
|
|
80
91
|
}): string;
|
|
92
|
+
/**
|
|
93
|
+
* Detects whether a document body carries any depth-free `media/<storageKey>` references.
|
|
94
|
+
*
|
|
95
|
+
* Used by pull to preserve the depth-free spelling for articles that already carry it, because the cache
|
|
96
|
+
* records exactly those bytes — re-spelling an untouched file would report a media rewrite nobody made. Once a
|
|
97
|
+
* document carries the article-relative form (or no local ref at all), the article-relative spelling takes over.
|
|
98
|
+
*/
|
|
99
|
+
export declare function carriesDepthFreeMediaRef(content: string): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Reports whether any local copy of an article still carries the depth-free spelling, deciding which form a
|
|
102
|
+
* re-pull must reproduce. A genuinely new article has no local copy, so it gets the article-relative form.
|
|
103
|
+
*/
|
|
104
|
+
export declare function shouldPreferLegacyRef(workspaceRoot: string, existingRelPaths: string[]): boolean;
|
|
81
105
|
//# sourceMappingURL=media-body-refs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-body-refs.d.ts","sourceRoot":"","sources":["../../src/utils/media-body-refs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"media-body-refs.d.ts","sourceRoot":"","sources":["../../src/utils/media-body-refs.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE7F,gGAAgG;AAChG,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,WAAW,CAAC;AAmBzC,2FAA2F;AAC3F,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrE;AAED,kGAAkG;AAClG,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE;QAAE,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA,MAAM,CA+BR;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,GACrC,MAAM,GAAG,IAAI,CAsBf;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,WAAW,GAAE,eAAoC,GAChD,MAAM,EAAE,CAEV;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAIvE;AAyBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IACP,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAE;QAAE,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACvC,6GAA6G;IAC7G,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GACA,MAAM,CAiBR;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CASjE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,OAAO,CAWhG"}
|
|
@@ -40,7 +40,10 @@
|
|
|
40
40
|
* form (`media/<storageKey>`) is still accepted wherever a local reference is read — workspaces built with it
|
|
41
41
|
* are re-spelled on the next pull rather than reported as edits.
|
|
42
42
|
*/
|
|
43
|
+
import fs from 'node:fs';
|
|
44
|
+
import path from 'node:path';
|
|
43
45
|
import { storageKeyToMarkdownRef } from './naming.js';
|
|
46
|
+
import { FENCE_POLICY, scanRawMediaReferences } from './media-refs.js';
|
|
44
47
|
/** Canonical root-relative media route prefix EmDash writes into Portable Text image blocks. */
|
|
45
48
|
export const MEDIA_FILE_ROUTE_PREFIX = '/_emdash/api/media/file/';
|
|
46
49
|
/** Workspace-relative directory a mirrored asset lives in. */
|
|
@@ -174,6 +177,18 @@ export function storageKeyFromMirrorRef(rawPath, candidateStorageKeys) {
|
|
|
174
177
|
}
|
|
175
178
|
return null;
|
|
176
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Scans a Markdown article body for every media reference it carries.
|
|
182
|
+
*
|
|
183
|
+
* This is the workspace-contract entry point Layer 2 consumers (delete, doctor, media) call instead of
|
|
184
|
+
* reaching into each other: markdown images and HTML `<img>` tags anywhere in the body, including inside
|
|
185
|
+
* opaque `<!--ec:block ... -->` fences and legacy container lines such as `[dimage url="..." control=true]`.
|
|
186
|
+
* External `http(s)` and `data:` URLs are excluded because they name no local mirror. The fence policy is
|
|
187
|
+
* delegated to `utils/media-refs.ts` so the guard/diagnostic distinction stays declared in exactly one place.
|
|
188
|
+
*/
|
|
189
|
+
export function scanArticleMediaReferences(bodyMarkdown, fencePolicy = FENCE_POLICY.guard) {
|
|
190
|
+
return scanRawMediaReferences(bodyMarkdown, fencePolicy);
|
|
191
|
+
}
|
|
177
192
|
/**
|
|
178
193
|
* Builds the `../../` depth an article at `articleRelPath` needs to reach `media/` from its own directory,
|
|
179
194
|
* matching the prefix the draft-image guards already accept.
|
|
@@ -237,4 +252,40 @@ export function localizeArticleMediaRefs(body, options) {
|
|
|
237
252
|
articleRelPath: options.preferLegacyRef ? undefined : options.articleRelPath,
|
|
238
253
|
});
|
|
239
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Detects whether a document body carries any depth-free `media/<storageKey>` references.
|
|
257
|
+
*
|
|
258
|
+
* Used by pull to preserve the depth-free spelling for articles that already carry it, because the cache
|
|
259
|
+
* records exactly those bytes — re-spelling an untouched file would report a media rewrite nobody made. Once a
|
|
260
|
+
* document carries the article-relative form (or no local ref at all), the article-relative spelling takes over.
|
|
261
|
+
*/
|
|
262
|
+
export function carriesDepthFreeMediaRef(content) {
|
|
263
|
+
for (const rawLine of content.split('\n')) {
|
|
264
|
+
const match = rawLine.trim().match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
|
|
265
|
+
if (!match)
|
|
266
|
+
continue;
|
|
267
|
+
const ref = match[2].trim();
|
|
268
|
+
// A depth-free `media/...` reference is the legacy spelling; anything carrying `../` already migrated.
|
|
269
|
+
if (isLocalMediaRef(ref) && ref.startsWith('media/'))
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Reports whether any local copy of an article still carries the depth-free spelling, deciding which form a
|
|
276
|
+
* re-pull must reproduce. A genuinely new article has no local copy, so it gets the article-relative form.
|
|
277
|
+
*/
|
|
278
|
+
export function shouldPreferLegacyRef(workspaceRoot, existingRelPaths) {
|
|
279
|
+
for (const relPath of existingRelPaths) {
|
|
280
|
+
try {
|
|
281
|
+
if (carriesDepthFreeMediaRef(fs.readFileSync(path.join(workspaceRoot, relPath), 'utf8'))) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
// Ignore unreadable files
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
240
291
|
//# sourceMappingURL=media-body-refs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-body-refs.js","sourceRoot":"","sources":["../../src/utils/media-body-refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"media-body-refs.js","sourceRoot":"","sources":["../../src/utils/media-body-refs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovers a storageKey from a media record's metadata map or companion YAML document.
|
|
3
|
+
*
|
|
4
|
+
* EmDash media records have two identifier spaces: the D1 primary key (`id`) and the R2 object key
|
|
5
|
+
* (`storageKey`). Modern companions record both fields explicitly, but older mirrors — or records
|
|
6
|
+
* written before Emit persisted the field — carry no `storageKey:` key at all, yet their `url:` is the
|
|
7
|
+
* canonical `/_emdash/api/media/file/<storageKey>` route, which is confirmed metadata written from the
|
|
8
|
+
* server response and not an inference from a body reference. Absolute and root-relative forms are both
|
|
9
|
+
* accepted because either can appear depending on how the record was mirrored. `legacy_*` residue is
|
|
10
|
+
* never read here: it backs no real record, so it must not serve as verification evidence.
|
|
11
|
+
*/
|
|
12
|
+
export declare function storageKeyFromMediaMeta(doc: Record<string, unknown>): string | undefined;
|
|
13
|
+
//# sourceMappingURL=media-companion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-companion.d.ts","sourceRoot":"","sources":["../../src/utils/media-companion.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAOxF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 SPORT-SERVICE RS★R
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Recovers a storageKey from a media record's metadata map or companion YAML document.
|
|
24
|
+
*
|
|
25
|
+
* EmDash media records have two identifier spaces: the D1 primary key (`id`) and the R2 object key
|
|
26
|
+
* (`storageKey`). Modern companions record both fields explicitly, but older mirrors — or records
|
|
27
|
+
* written before Emit persisted the field — carry no `storageKey:` key at all, yet their `url:` is the
|
|
28
|
+
* canonical `/_emdash/api/media/file/<storageKey>` route, which is confirmed metadata written from the
|
|
29
|
+
* server response and not an inference from a body reference. Absolute and root-relative forms are both
|
|
30
|
+
* accepted because either can appear depending on how the record was mirrored. `legacy_*` residue is
|
|
31
|
+
* never read here: it backs no real record, so it must not serve as verification evidence.
|
|
32
|
+
*/
|
|
33
|
+
export function storageKeyFromMediaMeta(doc) {
|
|
34
|
+
if (typeof doc.storageKey === 'string' && doc.storageKey.trim()) {
|
|
35
|
+
return doc.storageKey.trim();
|
|
36
|
+
}
|
|
37
|
+
if (typeof doc.url !== 'string' || !doc.url.trim())
|
|
38
|
+
return undefined;
|
|
39
|
+
const match = doc.url.match(/(?:^|\/)media\/file\/([^?#]+)/);
|
|
40
|
+
return match ? match[1] : undefined;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=media-companion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-companion.js","sourceRoot":"","sources":["../../src/utils/media-companion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAA4B;IAClE,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QAChE,OAAO,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IACrE,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmDash media record primary key (D1 database record ID).
|
|
3
|
+
* Always a 26-character canonical Crockford Base32 ULID.
|
|
4
|
+
*/
|
|
5
|
+
export type MediaRecordId = string & {
|
|
6
|
+
readonly __brand: 'MediaRecordId';
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* EmDash public R2 storage key (object key on Cloudflare R2).
|
|
10
|
+
* Formatted as `<ulid>.<ext>` or `<ulid1>.<ulid2>.<ext>`.
|
|
11
|
+
*/
|
|
12
|
+
export type StorageKey = string & {
|
|
13
|
+
readonly __brand: 'StorageKey';
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Unvalidated cast kept only for call sites that have already proven the value is a record id through
|
|
17
|
+
* confirmed metadata (a `media_state` row written by Emit, or a companion YAML `id:`). New code should go
|
|
18
|
+
* through `toMediaRecordId`, which refuses anything that is not a canonical ULID.
|
|
19
|
+
*/
|
|
20
|
+
export declare function asMediaRecordId(value: string): MediaRecordId;
|
|
21
|
+
/**
|
|
22
|
+
* Unvalidated cast for storage keys, mirroring `asMediaRecordId`: the brand is granted without checking the
|
|
23
|
+
* key shape. During gradual adoption it is used where a value already flows from a source Emit trusts (an
|
|
24
|
+
* upload target, a cached `media_state` row) and strict validation would abort otherwise-safe pushes; those
|
|
25
|
+
* sites are scheduled to move to `toStorageKey` in a later pass. New code should prefer `toStorageKey`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function asStorageKey(value: string): StorageKey;
|
|
28
|
+
export declare function isMediaRecordId(value: unknown): value is MediaRecordId;
|
|
29
|
+
/**
|
|
30
|
+
* Validates and normalizes a raw value into a `StorageKey`.
|
|
31
|
+
*
|
|
32
|
+
* A storageKey is the R2 object key that a canonical body URL addresses: exactly one path segment ending in
|
|
33
|
+
* a known image extension, with ULID-shaped segments (`<ulid>.<ext>` or `<ulid1>.<ulid2>.<ext>`). Migrated
|
|
34
|
+
* corpora also carry path-shaped keys (`uploads/2026/11/sample.png`), which are accepted as-is because they
|
|
35
|
+
* are the key the server itself reports. Returns null for anything with no usable key at all — an empty or
|
|
36
|
+
* whitespace-only value, a multi-segment value that is not a plain key, or a key carrying no extension Emit
|
|
37
|
+
* recognizes as media.
|
|
38
|
+
*/
|
|
39
|
+
export declare function toStorageKey(value: unknown): StorageKey | null;
|
|
40
|
+
/**
|
|
41
|
+
* Validates a raw value and returns it as a `MediaRecordId`, or null when it is not a canonical 26-character
|
|
42
|
+
* ULID. Unlike `asMediaRecordId`, this never trusts the caller: the brand is only granted to values that are
|
|
43
|
+
* provably record ids, so a storageKey-shaped or truncated value cannot be laundered into one.
|
|
44
|
+
*/
|
|
45
|
+
export declare function toMediaRecordId(value: unknown): MediaRecordId | null;
|
|
46
|
+
//# sourceMappingURL=media-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-id.d.ts","sourceRoot":"","sources":["../../src/utils/media-id.ts"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;CAAE,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAErE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEtD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAEtE;AAKD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAY9D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAGpE"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 SPORT-SERVICE RS★R
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
import { isCanonicalUlid } from './ulid.js';
|
|
23
|
+
/**
|
|
24
|
+
* Unvalidated cast kept only for call sites that have already proven the value is a record id through
|
|
25
|
+
* confirmed metadata (a `media_state` row written by Emit, or a companion YAML `id:`). New code should go
|
|
26
|
+
* through `toMediaRecordId`, which refuses anything that is not a canonical ULID.
|
|
27
|
+
*/
|
|
28
|
+
export function asMediaRecordId(value) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Unvalidated cast for storage keys, mirroring `asMediaRecordId`: the brand is granted without checking the
|
|
33
|
+
* key shape. During gradual adoption it is used where a value already flows from a source Emit trusts (an
|
|
34
|
+
* upload target, a cached `media_state` row) and strict validation would abort otherwise-safe pushes; those
|
|
35
|
+
* sites are scheduled to move to `toStorageKey` in a later pass. New code should prefer `toStorageKey`.
|
|
36
|
+
*/
|
|
37
|
+
export function asStorageKey(value) {
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
export function isMediaRecordId(value) {
|
|
41
|
+
return isCanonicalUlid(value);
|
|
42
|
+
}
|
|
43
|
+
/** Image extensions a storageKey may carry, matching the mirror naming rule. */
|
|
44
|
+
const STORAGE_KEY_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.avif', '.svg']);
|
|
45
|
+
/**
|
|
46
|
+
* Validates and normalizes a raw value into a `StorageKey`.
|
|
47
|
+
*
|
|
48
|
+
* A storageKey is the R2 object key that a canonical body URL addresses: exactly one path segment ending in
|
|
49
|
+
* a known image extension, with ULID-shaped segments (`<ulid>.<ext>` or `<ulid1>.<ulid2>.<ext>`). Migrated
|
|
50
|
+
* corpora also carry path-shaped keys (`uploads/2026/11/sample.png`), which are accepted as-is because they
|
|
51
|
+
* are the key the server itself reports. Returns null for anything with no usable key at all — an empty or
|
|
52
|
+
* whitespace-only value, a multi-segment value that is not a plain key, or a key carrying no extension Emit
|
|
53
|
+
* recognizes as media.
|
|
54
|
+
*/
|
|
55
|
+
export function toStorageKey(value) {
|
|
56
|
+
if (typeof value !== 'string')
|
|
57
|
+
return null;
|
|
58
|
+
const trimmed = value.trim();
|
|
59
|
+
if (!trimmed)
|
|
60
|
+
return null;
|
|
61
|
+
if (/[?#]/.test(trimmed))
|
|
62
|
+
return null;
|
|
63
|
+
const segments = trimmed.replace(/\\/g, '/').split('/');
|
|
64
|
+
if (segments.some((segment) => !segment))
|
|
65
|
+
return null;
|
|
66
|
+
const last = segments[segments.length - 1];
|
|
67
|
+
const extMatch = last.match(/\.[A-Za-z0-9]+$/);
|
|
68
|
+
if (!extMatch)
|
|
69
|
+
return null;
|
|
70
|
+
if (!STORAGE_KEY_EXTENSIONS.has(extMatch[0].toLowerCase()))
|
|
71
|
+
return null;
|
|
72
|
+
return trimmed;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validates a raw value and returns it as a `MediaRecordId`, or null when it is not a canonical 26-character
|
|
76
|
+
* ULID. Unlike `asMediaRecordId`, this never trusts the caller: the brand is only granted to values that are
|
|
77
|
+
* provably record ids, so a storageKey-shaped or truncated value cannot be laundered into one.
|
|
78
|
+
*/
|
|
79
|
+
export function toMediaRecordId(value) {
|
|
80
|
+
if (!isCanonicalUlid(value))
|
|
81
|
+
return null;
|
|
82
|
+
return value.trim().toUpperCase();
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=media-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-id.js","sourceRoot":"","sources":["../../src/utils/media-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAc5C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,KAAsB,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,KAAmB,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,gFAAgF;AAChF,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAEpG;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxD,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC/C,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO,OAAqB,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,OAAQ,KAAgB,CAAC,IAAI,EAAE,CAAC,WAAW,EAAmB,CAAC;AACjE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"AAyBA;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYvE;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAgC,GACzC,MAAM,CA+BR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAmBzE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIzE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAAoC,GAC7C,MAAM,GAAG,IAAI,CAyBf;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGzF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW7E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAC/B,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/E"}
|
package/dist/utils/naming.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* SOFTWARE.
|
|
21
21
|
*/
|
|
22
22
|
import path from 'node:path';
|
|
23
|
+
import { extractFilenameUlid } from './ulid.js';
|
|
23
24
|
/**
|
|
24
25
|
* Default maximum UTF-8 byte length for the title portion in generated filenames.
|
|
25
26
|
* Standard article filename: <yyyyMMdd_ (9B)><ulid (26B)>-<title (<=120B)>.md (3B) = <=158B,
|
|
@@ -174,9 +175,9 @@ export function collisionSuffixedMarkdownRef(markdownRef, mediaId) {
|
|
|
174
175
|
*/
|
|
175
176
|
export function extractUlidFromFilename(filenameOrPath) {
|
|
176
177
|
const baseWithoutDate = stripDatePrefix(filenameOrPath);
|
|
177
|
-
const match = baseWithoutDate
|
|
178
|
+
const match = extractFilenameUlid(baseWithoutDate);
|
|
178
179
|
if (match)
|
|
179
|
-
return match
|
|
180
|
+
return match;
|
|
180
181
|
const firstHyphen = baseWithoutDate.indexOf('-');
|
|
181
182
|
const candidate = firstHyphen !== -1 ? baseWithoutDate.slice(0, firstHyphen) : baseWithoutDate;
|
|
182
183
|
if (candidate.length === 26) {
|
package/dist/utils/naming.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical 26-character Crockford Base32 pattern for ULIDs:
|
|
3
|
+
* Excludes I, L, O, and U to prevent human transcription errors.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CANONICAL_ULID_PATTERN = "[0-9A-HJ-NP-Z]{26}";
|
|
6
|
+
/**
|
|
7
|
+
* Case-insensitive regular expression validating a 26-character canonical ULID string.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CANONICAL_ULID_REGEX: RegExp;
|
|
10
|
+
/**
|
|
11
|
+
* Case-sensitive (uppercase only) regular expression validating a 26-character canonical ULID string.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CANONICAL_ULID_EXACT_REGEX: RegExp;
|
|
14
|
+
/**
|
|
15
|
+
* Global matcher for finding 26-character canonical ULIDs within URLs, storageKeys, or filenames.
|
|
16
|
+
*/
|
|
17
|
+
export declare const CANONICAL_ULID_GLOBAL_REGEX: RegExp;
|
|
18
|
+
/**
|
|
19
|
+
* Reports whether a value is a valid 26-character canonical Crockford Base32 ULID.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isCanonicalUlid(value: unknown): value is string;
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes a string to uppercase canonical ULID if valid, or returns null.
|
|
24
|
+
*/
|
|
25
|
+
export declare function toCanonicalUlid(value: unknown): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Extracts a 26-character ID from a filename stem.
|
|
28
|
+
* Preserves permissive naming rules ([0-9A-Za-z_-]{26}) for legacy compatibility (EMIT naming.ts:203).
|
|
29
|
+
*/
|
|
30
|
+
export declare function extractFilenameUlid(filename: string): string | null;
|
|
31
|
+
//# sourceMappingURL=ulid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ulid.d.ts","sourceRoot":"","sources":["../../src/utils/ulid.ts"],"names":[],"mappings":"AAsBA;;;GAGG;AACH,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAA0B,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,0BAA0B,QAAyB,CAAC;AAEjE;;GAEG;AACH,eAAO,MAAM,2BAA2B,QAAyB,CAAC;AAElE;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAG/D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI7D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAInE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 SPORT-SERVICE RS★R
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Canonical 26-character Crockford Base32 pattern for ULIDs:
|
|
24
|
+
* Excludes I, L, O, and U to prevent human transcription errors.
|
|
25
|
+
*/
|
|
26
|
+
export const CANONICAL_ULID_PATTERN = '[0-9A-HJ-NP-Z]{26}';
|
|
27
|
+
/**
|
|
28
|
+
* Case-insensitive regular expression validating a 26-character canonical ULID string.
|
|
29
|
+
*/
|
|
30
|
+
export const CANONICAL_ULID_REGEX = /^[0-9A-HJ-NP-Z]{26}$/i;
|
|
31
|
+
/**
|
|
32
|
+
* Case-sensitive (uppercase only) regular expression validating a 26-character canonical ULID string.
|
|
33
|
+
*/
|
|
34
|
+
export const CANONICAL_ULID_EXACT_REGEX = /^[0-9A-HJ-NP-Z]{26}$/;
|
|
35
|
+
/**
|
|
36
|
+
* Global matcher for finding 26-character canonical ULIDs within URLs, storageKeys, or filenames.
|
|
37
|
+
*/
|
|
38
|
+
export const CANONICAL_ULID_GLOBAL_REGEX = /[0-9A-HJ-NP-Z]{26}/gi;
|
|
39
|
+
/**
|
|
40
|
+
* Reports whether a value is a valid 26-character canonical Crockford Base32 ULID.
|
|
41
|
+
*/
|
|
42
|
+
export function isCanonicalUlid(value) {
|
|
43
|
+
if (typeof value !== 'string')
|
|
44
|
+
return false;
|
|
45
|
+
return CANONICAL_ULID_REGEX.test(value.trim());
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Normalizes a string to uppercase canonical ULID if valid, or returns null.
|
|
49
|
+
*/
|
|
50
|
+
export function toCanonicalUlid(value) {
|
|
51
|
+
if (typeof value !== 'string')
|
|
52
|
+
return null;
|
|
53
|
+
const trimmed = value.trim();
|
|
54
|
+
return CANONICAL_ULID_REGEX.test(trimmed) ? trimmed.toUpperCase() : null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Extracts a 26-character ID from a filename stem.
|
|
58
|
+
* Preserves permissive naming rules ([0-9A-Za-z_-]{26}) for legacy compatibility (EMIT naming.ts:203).
|
|
59
|
+
*/
|
|
60
|
+
export function extractFilenameUlid(filename) {
|
|
61
|
+
const baseWithoutDate = filename.replace(/^\d{8}_/, '');
|
|
62
|
+
const match = baseWithoutDate.match(/^([0-9A-Za-z_-]{26})(?:-|\.|$)/);
|
|
63
|
+
return match ? match[1] : null;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ulid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ulid.js","sourceRoot":"","sources":["../../src/utils/ulid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAE5D;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,sBAAsB,CAAC;AAEjE;;GAEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAElE;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACtE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC"}
|
package/package.json
CHANGED