@filen/utils 0.0.8 → 0.0.9

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.
@@ -40,7 +40,8 @@ export class ChecklistParser {
40
40
  html += `<ul data-checked="${item.checked}">`;
41
41
  currentCheckedStatus = item.checked;
42
42
  }
43
- html += `<li>${item.content.trim().length > 0 ? item.content.trim() : "<br>"}</li>`;
43
+ const trimmed = item.content.trim();
44
+ html += `<li>${trimmed.length > 0 ? trimmed : "<br>"}</li>`;
44
45
  }
45
46
  if (checklist.length > 0) {
46
47
  html += "</ul>";
package/dist/misc.js CHANGED
@@ -88,6 +88,7 @@ const RESERVED_NAMES_WINDOWS = new Set([
88
88
  "LPT9"
89
89
  ]);
90
90
  const textEncoder = new TextEncoder();
91
+ const textDecoder = new TextDecoder("utf-8", { fatal: false });
91
92
  export function sanitizeFileName(filename, replacement = "_") {
92
93
  let sanitizedFilename = filename.normalize("NFC");
93
94
  sanitizedFilename = sanitizedFilename.replace(ZERO_WIDTH_AND_CONTROL_RE, "");
@@ -100,10 +101,9 @@ export function sanitizeFileName(filename, replacement = "_") {
100
101
  sanitizedFilename += replacement;
101
102
  }
102
103
  const maxByteLength = 255;
103
- let byteLength = textEncoder.encode(sanitizedFilename).length;
104
- while (byteLength > maxByteLength && sanitizedFilename.length > 0) {
105
- sanitizedFilename = sanitizedFilename.slice(0, -1);
106
- byteLength = textEncoder.encode(sanitizedFilename).length;
104
+ const encoded = textEncoder.encode(sanitizedFilename);
105
+ if (encoded.length > maxByteLength) {
106
+ sanitizedFilename = textDecoder.decode(encoded.subarray(0, maxByteLength)).replace(/\uFFFD/g, "");
107
107
  }
108
108
  if (!sanitizedFilename) {
109
109
  return "file";
@@ -139,27 +139,31 @@ export function extractLinksFromString(text) {
139
139
  }
140
140
  return results;
141
141
  }
142
+ const HAS_UPPERCASE_RE = /[A-Z]/;
143
+ const HAS_LOWERCASE_RE = /[a-z]/;
144
+ const HAS_SPECIAL_CHARS_RE = /[!@#$%^&*(),.?":{}|<>]/;
145
+ const YOUTUBE_VIDEO_ID_RE = /(?:\?v=|\/embed\/|\/watch\?v=|\/\w+\/\w+\/|youtu.be\/)([\w-]{11})/;
142
146
  export function parseYouTubeVideoId(url) {
143
- const regExp = /(?:\?v=|\/embed\/|\/watch\?v=|\/\w+\/\w+\/|youtu.be\/)([\w-]{11})/;
144
- const match = url.match(regExp);
147
+ const match = url.match(YOUTUBE_VIDEO_ID_RE);
145
148
  if (match && match.length === 2 && match[1]) {
146
149
  return match[1];
147
150
  }
148
151
  return null;
149
152
  }
153
+ const FILEN_PUBLIC_LINK_RE = /https?:\/\/(?:app|drive)\.filen\.io\/#\/([df])\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:%23|#)([A-Za-z0-9]{32,})/;
154
+ const HEX_64_RE = /^[0-9A-Fa-f]{64}$/;
150
155
  export function parseFilenPublicLink(url) {
151
156
  if (!url || url.length === 0) {
152
157
  return null;
153
158
  }
154
- const filenRegex = /https?:\/\/(?:app|drive)\.filen\.io\/#\/([df])\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:%23|#)([A-Za-z0-9]{32,})/;
155
- const match = filenRegex.exec(url);
159
+ const match = FILEN_PUBLIC_LINK_RE.exec(url);
156
160
  if (!match || match.length < 4 || !match[1] || !match[2] || !match[3]) {
157
161
  return null;
158
162
  }
159
163
  const pathType = match[1];
160
164
  const uuid = match[2];
161
165
  let key = match[3];
162
- if (/^[0-9A-Fa-f]{64}$/.test(key)) {
166
+ if (HEX_64_RE.test(key)) {
163
167
  try {
164
168
  key = Buffer.from(key, "hex").toString("utf8");
165
169
  }
@@ -185,9 +189,9 @@ export function parseXStatusId(url) {
185
189
  return part.trim();
186
190
  }
187
191
  export function ratePasswordStrength(password) {
188
- const hasUppercase = /[A-Z]/.test(password);
189
- const hasLowercase = /[a-z]/.test(password);
190
- const hasSpecialChars = /[!@#$%^&*(),.?":{}|<>]/.test(password);
192
+ const hasUppercase = HAS_UPPERCASE_RE.test(password);
193
+ const hasLowercase = HAS_LOWERCASE_RE.test(password);
194
+ const hasSpecialChars = HAS_SPECIAL_CHARS_RE.test(password);
191
195
  const length = password.length;
192
196
  let strength = "weak";
193
197
  if (length >= 10 && hasUppercase && hasLowercase && hasSpecialChars) {
package/dist/notes.js CHANGED
@@ -12,12 +12,9 @@ export function createNotePreviewFromContentText(type, content) {
12
12
  }
13
13
  if (type === "checklist") {
14
14
  const ex = content
15
- .split('<ul data-checked="false">')
16
- .join("")
17
- .split('<ul data-checked="true">')
18
- .join("")
19
- .split("\n")
20
- .join("")
15
+ .replaceAll('<ul data-checked="false">', "")
16
+ .replaceAll('<ul data-checked="true">', "")
17
+ .replaceAll("\n", "")
21
18
  .split("<li>");
22
19
  for (const listPoint of ex) {
23
20
  const listPointEx = listPoint.split("</li>");
package/dist/run.js CHANGED
@@ -183,12 +183,10 @@ export async function runRetry(fn, options) {
183
183
  const maxAttempts = options?.maxAttempts ?? 3;
184
184
  const delayMs = options?.delayMs ?? 1000;
185
185
  const backoff = options?.backoff ?? "exponential";
186
+ const runOptions = { onError: options?.onError, throw: false };
186
187
  let lastError = null;
187
188
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
188
- const result = await run(defer => fn(defer, attempt), {
189
- ...options,
190
- throw: false
191
- });
189
+ const result = await run(defer => fn(defer, attempt), runOptions);
192
190
  if (result.success) {
193
191
  return result;
194
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@filen/utils",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "A collection of utils for Filen",
5
5
  "private": false,
6
6
  "repository": {