@aspan-corporation/ac-shared 1.2.29 → 1.2.31
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/lib/utils/diary.d.ts +53 -0
- package/lib/utils/diary.js +125 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/lib/utils/thumbsKey.d.ts +1 -0
- package/lib/utils/thumbsKey.js +7 -1
- package/package.json +5 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diary entries are Markdown files stored in a dedicated diary bucket at keys
|
|
3
|
+
* `diary/YYYY/MM/YYYYMMDD.md`. They are indexed in the shared metadata table so
|
|
4
|
+
* they are searchable through the same interface as photos. Body text is made
|
|
5
|
+
* searchable by tokenising it into `ac:text:<word>` tags that ride the existing
|
|
6
|
+
* inverted-index search table.
|
|
7
|
+
*/
|
|
8
|
+
/** Key prefix for every diary object (and the `folder`/id namespace). */
|
|
9
|
+
export declare const DIARY_PREFIX = "diary/";
|
|
10
|
+
/** Marks a meta item as a diary entry (value "true"). */
|
|
11
|
+
export declare const TAG_DIARY_ENTRY = "ac:diary:entry";
|
|
12
|
+
/** Human title of the entry. */
|
|
13
|
+
export declare const TAG_DIARY_TITLE = "ac:diary:title";
|
|
14
|
+
/** Short body preview for card display (first ~160 chars, no markdown). */
|
|
15
|
+
export declare const TAG_DIARY_PREVIEW = "ac:diary:preview";
|
|
16
|
+
/** One per embedded photo: value is the referenced media key. */
|
|
17
|
+
export declare const TAG_DIARY_PHOTO = "ac:diary:photo";
|
|
18
|
+
/** Namespace for body word tokens: `ac:text:<word>`. */
|
|
19
|
+
export declare const TEXT_TOKEN_PREFIX = "ac:text:";
|
|
20
|
+
/** Max body characters surfaced in the preview tag. */
|
|
21
|
+
export declare const DIARY_PREVIEW_LENGTH = 160;
|
|
22
|
+
/** Upper bound on distinct word tokens written per entry (bounds item size). */
|
|
23
|
+
export declare const MAX_TEXT_TOKENS = 400;
|
|
24
|
+
/**
|
|
25
|
+
* Compute the diary object key for a date.
|
|
26
|
+
* new Date("2026-06-15") → "diary/2026/06/20260615.md"
|
|
27
|
+
* Accepts a Date or an ISO/`YYYY-MM-DD` string. Uses UTC parts so the key is
|
|
28
|
+
* stable regardless of the runtime timezone.
|
|
29
|
+
*/
|
|
30
|
+
export declare const diaryKey: (date: Date | string) => string;
|
|
31
|
+
/** True when an id/key is a diary entry object. */
|
|
32
|
+
export declare const isDiaryKey: (key: string) => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Parse the date out of a diary key (the inverse of `diaryKey`).
|
|
35
|
+
* "diary/2026/06/20260615.md" → { year: 2026, month: 6, day: 15 }
|
|
36
|
+
* Returns undefined when the key isn't a recognised diary key.
|
|
37
|
+
*/
|
|
38
|
+
export declare const parseDiaryKeyDate: (key: string) => {
|
|
39
|
+
year: number;
|
|
40
|
+
month: number;
|
|
41
|
+
day: number;
|
|
42
|
+
} | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Tokenise diary markdown into a deduplicated set of searchable lowercase
|
|
45
|
+
* words. Strips markdown syntax (code fences, image/link targets, formatting),
|
|
46
|
+
* drops stop-words and tokens shorter than 2 chars, and caps the result at
|
|
47
|
+
* `MAX_TEXT_TOKENS` to bound the meta-item size. No stemming (exact-word match).
|
|
48
|
+
*/
|
|
49
|
+
export declare const tokenizeText: (markdown: string) => string[];
|
|
50
|
+
/** Plain-text preview: strip markdown, collapse whitespace, truncate. */
|
|
51
|
+
export declare const diaryPreview: (markdown: string) => string;
|
|
52
|
+
/** Extract the media keys of photos embedded as `` in the body. */
|
|
53
|
+
export declare const extractEmbeddedPhotoKeys: (markdown: string) => string[];
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diary entries are Markdown files stored in a dedicated diary bucket at keys
|
|
3
|
+
* `diary/YYYY/MM/YYYYMMDD.md`. They are indexed in the shared metadata table so
|
|
4
|
+
* they are searchable through the same interface as photos. Body text is made
|
|
5
|
+
* searchable by tokenising it into `ac:text:<word>` tags that ride the existing
|
|
6
|
+
* inverted-index search table.
|
|
7
|
+
*/
|
|
8
|
+
/** Key prefix for every diary object (and the `folder`/id namespace). */
|
|
9
|
+
export const DIARY_PREFIX = "diary/";
|
|
10
|
+
/** Marks a meta item as a diary entry (value "true"). */
|
|
11
|
+
export const TAG_DIARY_ENTRY = "ac:diary:entry";
|
|
12
|
+
/** Human title of the entry. */
|
|
13
|
+
export const TAG_DIARY_TITLE = "ac:diary:title";
|
|
14
|
+
/** Short body preview for card display (first ~160 chars, no markdown). */
|
|
15
|
+
export const TAG_DIARY_PREVIEW = "ac:diary:preview";
|
|
16
|
+
/** One per embedded photo: value is the referenced media key. */
|
|
17
|
+
export const TAG_DIARY_PHOTO = "ac:diary:photo";
|
|
18
|
+
/** Namespace for body word tokens: `ac:text:<word>`. */
|
|
19
|
+
export const TEXT_TOKEN_PREFIX = "ac:text:";
|
|
20
|
+
/** Max body characters surfaced in the preview tag. */
|
|
21
|
+
export const DIARY_PREVIEW_LENGTH = 160;
|
|
22
|
+
/** Upper bound on distinct word tokens written per entry (bounds item size). */
|
|
23
|
+
export const MAX_TEXT_TOKENS = 400;
|
|
24
|
+
const pad2 = (n) => String(n).padStart(2, "0");
|
|
25
|
+
/**
|
|
26
|
+
* Compute the diary object key for a date.
|
|
27
|
+
* new Date("2026-06-15") → "diary/2026/06/20260615.md"
|
|
28
|
+
* Accepts a Date or an ISO/`YYYY-MM-DD` string. Uses UTC parts so the key is
|
|
29
|
+
* stable regardless of the runtime timezone.
|
|
30
|
+
*/
|
|
31
|
+
export const diaryKey = (date) => {
|
|
32
|
+
const d = typeof date === "string" ? new Date(date) : date;
|
|
33
|
+
if (isNaN(d.getTime())) {
|
|
34
|
+
throw new Error(`diaryKey: invalid date "${String(date)}"`);
|
|
35
|
+
}
|
|
36
|
+
const y = d.getUTCFullYear();
|
|
37
|
+
const m = pad2(d.getUTCMonth() + 1);
|
|
38
|
+
const day = pad2(d.getUTCDate());
|
|
39
|
+
return `${DIARY_PREFIX}${y}/${m}/${y}${m}${day}.md`;
|
|
40
|
+
};
|
|
41
|
+
/** True when an id/key is a diary entry object. */
|
|
42
|
+
export const isDiaryKey = (key) => key.startsWith(DIARY_PREFIX) && key.endsWith(".md");
|
|
43
|
+
/**
|
|
44
|
+
* Parse the date out of a diary key (the inverse of `diaryKey`).
|
|
45
|
+
* "diary/2026/06/20260615.md" → { year: 2026, month: 6, day: 15 }
|
|
46
|
+
* Returns undefined when the key isn't a recognised diary key.
|
|
47
|
+
*/
|
|
48
|
+
export const parseDiaryKeyDate = (key) => {
|
|
49
|
+
const m = /diary\/(\d{4})\/(\d{2})\/(\d{4})(\d{2})(\d{2})\.md$/.exec(key);
|
|
50
|
+
if (!m)
|
|
51
|
+
return undefined;
|
|
52
|
+
return { year: Number(m[3]), month: Number(m[4]), day: Number(m[5]) };
|
|
53
|
+
};
|
|
54
|
+
// A small English stop-word set — high-frequency words that add noise and bloat
|
|
55
|
+
// the index without improving recall for a personal diary.
|
|
56
|
+
const STOP_WORDS = new Set([
|
|
57
|
+
"the", "and", "for", "are", "but", "not", "you", "all", "any", "can", "had",
|
|
58
|
+
"her", "was", "one", "our", "out", "day", "get", "has", "him", "his", "how",
|
|
59
|
+
"its", "may", "new", "now", "old", "see", "two", "way", "who", "did", "yes",
|
|
60
|
+
"his", "she", "they", "them", "this", "that", "with", "have", "from", "your",
|
|
61
|
+
"were", "been", "their", "what", "when", "then", "than", "into", "just",
|
|
62
|
+
"like", "over", "also", "back", "after", "would", "could", "there", "here",
|
|
63
|
+
"about", "which", "while", "these", "those", "where", "very", "much", "some",
|
|
64
|
+
"such", "only", "more", "most", "will", "well", "went", "going", "got",
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* Tokenise diary markdown into a deduplicated set of searchable lowercase
|
|
68
|
+
* words. Strips markdown syntax (code fences, image/link targets, formatting),
|
|
69
|
+
* drops stop-words and tokens shorter than 2 chars, and caps the result at
|
|
70
|
+
* `MAX_TEXT_TOKENS` to bound the meta-item size. No stemming (exact-word match).
|
|
71
|
+
*/
|
|
72
|
+
export const tokenizeText = (markdown) => {
|
|
73
|
+
if (!markdown)
|
|
74
|
+
return [];
|
|
75
|
+
const stripped = markdown
|
|
76
|
+
// fenced + inline code
|
|
77
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
78
|
+
.replace(/`[^`]*`/g, " ")
|
|
79
|
+
// image / link targets: keep the visible text, drop the URL/key
|
|
80
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, " $1 ")
|
|
81
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, " $1 ")
|
|
82
|
+
// leftover markdown punctuation
|
|
83
|
+
.replace(/[#>*_~\-]+/g, " ");
|
|
84
|
+
const seen = new Set();
|
|
85
|
+
for (const raw of stripped.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
86
|
+
if (raw.length < 2)
|
|
87
|
+
continue;
|
|
88
|
+
// Drop pure numbers (dates/counts add noise); keep alphanumerics like "v2".
|
|
89
|
+
if (/^\d+$/.test(raw))
|
|
90
|
+
continue;
|
|
91
|
+
if (STOP_WORDS.has(raw))
|
|
92
|
+
continue;
|
|
93
|
+
seen.add(raw);
|
|
94
|
+
if (seen.size >= MAX_TEXT_TOKENS)
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
return [...seen];
|
|
98
|
+
};
|
|
99
|
+
/** Plain-text preview: strip markdown, collapse whitespace, truncate. */
|
|
100
|
+
export const diaryPreview = (markdown) => {
|
|
101
|
+
const text = markdown
|
|
102
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
103
|
+
.replace(/`[^`]*`/g, " ")
|
|
104
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, " ")
|
|
105
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, " $1 ")
|
|
106
|
+
.replace(/[#>*_~]+/g, " ")
|
|
107
|
+
.replace(/\s+/g, " ")
|
|
108
|
+
.trim();
|
|
109
|
+
return text.length > DIARY_PREVIEW_LENGTH
|
|
110
|
+
? text.slice(0, DIARY_PREVIEW_LENGTH).trimEnd() + "…"
|
|
111
|
+
: text;
|
|
112
|
+
};
|
|
113
|
+
/** Extract the media keys of photos embedded as `` in the body. */
|
|
114
|
+
export const extractEmbeddedPhotoKeys = (markdown) => {
|
|
115
|
+
const keys = new Set();
|
|
116
|
+
const re = /!\[[^\]]*\]\(([^)]+)\)/g;
|
|
117
|
+
let m;
|
|
118
|
+
while ((m = re.exec(markdown)) !== null) {
|
|
119
|
+
const key = m[1].trim();
|
|
120
|
+
// Only treat library media keys as photo links (ignore external URLs).
|
|
121
|
+
if (key && !/^https?:\/\//i.test(key))
|
|
122
|
+
keys.add(key);
|
|
123
|
+
}
|
|
124
|
+
return [...keys];
|
|
125
|
+
};
|
package/lib/utils/index.d.ts
CHANGED
package/lib/utils/index.js
CHANGED
package/lib/utils/thumbsKey.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const EXTENSION_JPEG = "jpeg";
|
|
|
2
2
|
export declare const EXTENSION_JPG = "jpg";
|
|
3
3
|
export declare const EXTENSION_WEBP = "webp";
|
|
4
4
|
export declare const EXTENSION_HEIC = "heic";
|
|
5
|
+
export declare const EXTENSION_PNG = "png";
|
|
5
6
|
export declare const EXTENSION_MOV = "mov";
|
|
6
7
|
export declare const EXTENSION_MP4 = "mp4";
|
|
7
8
|
export declare const EXTENSION_THUMBS = "webp";
|
package/lib/utils/thumbsKey.js
CHANGED
|
@@ -2,14 +2,20 @@ export const EXTENSION_JPEG = "jpeg";
|
|
|
2
2
|
export const EXTENSION_JPG = "jpg";
|
|
3
3
|
export const EXTENSION_WEBP = "webp";
|
|
4
4
|
export const EXTENSION_HEIC = "heic";
|
|
5
|
+
export const EXTENSION_PNG = "png";
|
|
5
6
|
export const EXTENSION_MOV = "mov";
|
|
6
7
|
export const EXTENSION_MP4 = "mp4";
|
|
7
8
|
export const EXTENSION_THUMBS = EXTENSION_WEBP;
|
|
8
9
|
export const EXTENSION_ENCODED_VIDEO = EXTENSION_MP4;
|
|
10
|
+
// Image source formats the resizer can decode with Sharp/libvips. PNG is
|
|
11
|
+
// included (libvips decodes it natively). RAW/DNG is intentionally excluded —
|
|
12
|
+
// the sharp layer isn't built with a RAW loader (libraw), so those can't be
|
|
13
|
+
// decoded without a layer change.
|
|
9
14
|
export const ALLOWED_EXTENSIONS = [
|
|
10
15
|
EXTENSION_JPEG,
|
|
11
16
|
EXTENSION_JPG,
|
|
12
|
-
EXTENSION_HEIC
|
|
17
|
+
EXTENSION_HEIC,
|
|
18
|
+
EXTENSION_PNG
|
|
13
19
|
];
|
|
14
20
|
export const ALLOWED_VIDEO_EXTENSIONS = [EXTENSION_MOV, EXTENSION_MP4];
|
|
15
21
|
export const DIM_THUMBNAIL_WIDTH = 320;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aspan-corporation/ac-shared",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.31",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"exports": {
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
"./utils/thumbsKey": {
|
|
24
24
|
"types": "./lib/utils/thumbsKey.d.ts",
|
|
25
25
|
"import": "./lib/utils/thumbsKey.js"
|
|
26
|
+
},
|
|
27
|
+
"./utils/diary": {
|
|
28
|
+
"types": "./lib/utils/diary.d.ts",
|
|
29
|
+
"import": "./lib/utils/diary.js"
|
|
26
30
|
}
|
|
27
31
|
},
|
|
28
32
|
"author": "",
|