@shapio/local 0.5.9 → 0.5.10
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/{advisoryLocks-ajVNBhfa.js → advisoryLocks-DGbxw3_E.js} +13 -13
- package/dist/advisoryLocks-DGbxw3_E.js.map +1 -0
- package/dist/errors-nQvt9bs7.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/{migrator-CO5sXosL.js → migrator-BfVtncOo.js} +269 -148
- package/dist/migrator-BfVtncOo.js.map +1 -0
- package/package.json +4 -4
- package/dist/advisoryLocks-ajVNBhfa.js.map +0 -1
- package/dist/migrator-CO5sXosL.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors-nQvt9bs7.js","names":[],"sources":["../../../apps/api/src/media/keys.ts","../../../apps/api/src/media/errors.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\n\nexport type MediaVisibility = 'public' | 'private';\n\nconst MAX_FILENAME_LENGTH = 200;\nconst MAX_SLUG_LENGTH = 80;\n/** Characters Windows, macOS or shells treat specially, plus every control character. */\n// eslint-disable-next-line no-control-regex\nconst UNSAFE_FILENAME_CHARACTERS = /[\\u0000-\\u001f\\u007f<>:\"/\\\\|?*]+/g;\nconst SEGMENT = '[A-Za-z0-9][A-Za-z0-9._-]{0,127}';\nconst STORAGE_KEY_PATTERN = new RegExp(`^(public|private)(/${SEGMENT}){2,5}$`);\n\nconst splitExtension = (name: string): { stem: string; extension: string } => {\n const dot = name.lastIndexOf('.');\n return dot > 0 && name.length - dot <= 16\n ? { stem: name.slice(0, dot), extension: name.slice(dot) }\n : { stem: name, extension: '' };\n};\n\n/**\n * The display name kept for an upload: the last path segment, NFC-
|
|
1
|
+
{"version":3,"file":"errors-nQvt9bs7.js","names":[],"sources":["../../../apps/api/src/media/keys.ts","../../../apps/api/src/media/errors.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\n\nexport type MediaVisibility = 'public' | 'private';\n\nconst MAX_FILENAME_LENGTH = 200;\nconst MAX_SLUG_LENGTH = 80;\n/** Characters Windows, macOS or shells treat specially, plus every control character. */\n// eslint-disable-next-line no-control-regex\nconst UNSAFE_FILENAME_CHARACTERS = /[\\u0000-\\u001f\\u007f<>:\"/\\\\|?*]+/g;\nconst SEGMENT = '[A-Za-z0-9][A-Za-z0-9._-]{0,127}';\nconst STORAGE_KEY_PATTERN = new RegExp(`^(public|private)(/${SEGMENT}){2,5}$`);\n\nconst splitExtension = (name: string): { stem: string; extension: string } => {\n const dot = name.lastIndexOf('.');\n return dot > 0 && name.length - dot <= 16\n ? { stem: name.slice(0, dot), extension: name.slice(dot) }\n : { stem: name, extension: '' };\n};\n\n/**\n * The display name kept for an upload: the last path segment, NFC-normalized, without control or\n * path characters, at most 200 characters with the extension preserved. Never used as a storage path.\n */\nexport const sanitizeFilename = (input: string): string => {\n const base = input.split(/[/\\\\]/).pop() ?? '';\n const cleaned = base\n .normalize('NFC')\n .replace(UNSAFE_FILENAME_CHARACTERS, '-')\n .replace(/\\s+/g, ' ')\n .replace(/^[\\s.-]+|[\\s.]+$/g, '');\n if (cleaned === '') {\n return 'file';\n }\n if (cleaned.length <= MAX_FILENAME_LENGTH) {\n return cleaned;\n }\n const { stem, extension } = splitExtension(cleaned);\n return `${stem.slice(0, MAX_FILENAME_LENGTH - extension.length)}${extension}`;\n};\n\n/** An ASCII, lower-case, URL-safe version of a file name for storage keys: `Été 2026.JPG` → `ete-2026.jpg`. */\nexport const toStorageSlug = (filename: string): string => {\n const { stem, extension } = splitExtension(filename);\n const slugify = (value: string) =>\n value\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n const slugStem = slugify(stem).slice(0, MAX_SLUG_LENGTH) || 'file';\n const slugExtension = slugify(extension).slice(0, 15);\n return slugExtension ? `${slugStem}.${slugExtension}` : slugStem;\n};\n\n/** Relative keys only, made of safe segments under a visibility prefix: no `..`, no absolute paths. */\nexport const isValidStorageKey = (key: string): boolean => STORAGE_KEY_PATTERN.test(key);\n\n/**\n * `public/<assetId>/<token>/<slug>`. The visibility prefix lets an operator expose only `public/` through a\n * CDN or bucket policy; the random token gives every upload (and replacement) a new, cacheable URL.\n */\nexport const buildAssetKey = (visibility: MediaVisibility, assetId: string, filename: string): string =>\n `${visibility}/${assetId}/${randomBytes(12).toString('hex')}/${toStorageSlug(filename)}`;\n\n/** Variants sit beside their original: `public/<assetId>/<token>/v/<name>.<extension>`. */\nexport const buildVariantKey = (assetKey: string, name: string, extension: string): string =>\n `${assetKey.slice(0, assetKey.lastIndexOf('/'))}/v/${name}.${extension}`;\n\n/** The same key under the other visibility prefix (visibility changes move objects). */\nexport const withVisibility = (key: string, visibility: MediaVisibility): string =>\n `${visibility}${key.slice(key.indexOf('/'))}`;\n\nexport const visibilityOfKey = (key: string): MediaVisibility | undefined => {\n const prefix = key.slice(0, key.indexOf('/'));\n return prefix === 'public' || prefix === 'private' ? prefix : undefined;\n};\n\n/** Percent-encodes each segment for use in a URL path. */\nexport const encodeKeyPath = (key: string): string => key.split('/').map(encodeURIComponent).join('/');\n","/** Thrown by adapters when a key has no object, so callers can tell \"missing\" from \"storage is down\". */\nexport class ObjectNotFoundError extends Error {\n constructor(key: string, options?: ErrorOptions) {\n super(`No stored object at ${key}`, options);\n this.name = 'ObjectNotFoundError';\n }\n}\n\nexport class InvalidStorageKeyError extends Error {\n constructor(key: string) {\n super(`Not a valid media storage key: ${JSON.stringify(key)}`);\n this.name = 'InvalidStorageKeyError';\n }\n}\n"],"mappings":";;AAUA,MAAM,sBAAsB,IAAI,OAAO,4DAAsC;;AA8C7E,MAAa,qBAAqB,QAAyB,oBAAoB,KAAK,GAAG;;AAcvF,MAAa,kBAAkB,KAAa,eAC1C,GAAG,aAAa,IAAI,MAAM,IAAI,QAAQ,GAAG,CAAC;AAE5C,MAAa,mBAAmB,QAA6C;CAC3E,MAAM,SAAS,IAAI,MAAM,GAAG,IAAI,QAAQ,GAAG,CAAC;CAC5C,OAAO,WAAW,YAAY,WAAW,YAAY,SAAS,KAAA;AAChE;;AAGA,MAAa,iBAAiB,QAAwB,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;;;;AC9ErG,IAAa,sBAAb,cAAyC,MAAM;CAC7C,YAAY,KAAa,SAAwB;EAC/C,MAAM,uBAAuB,OAAO,OAAO;EAC3C,KAAK,OAAO;CACd;AACF;AAEA,IAAa,yBAAb,cAA4C,MAAM;CAChD,YAAY,KAAa;EACvB,MAAM,kCAAkC,KAAK,UAAU,GAAG,GAAG;EAC7D,KAAK,OAAO;CACd;AACF"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as assertNotNested, d as toDatetimeText, f as AppError, i as createMysqlDb, l as withConnectionScope, n as createSqliteDb, o as DEFAULT_POOL_ACQUIRE_TIMEOUT_MS, s as pgAcquireError, t as lockKeyFromId, u as toStoredTimestamp } from "./advisoryLocks-
|
|
1
|
+
import { c as assertNotNested, d as toDatetimeText, f as AppError, i as createMysqlDb, l as withConnectionScope, n as createSqliteDb, o as DEFAULT_POOL_ACQUIRE_TIMEOUT_MS, s as pgAcquireError, t as lockKeyFromId, u as toStoredTimestamp } from "./advisoryLocks-DGbxw3_E.js";
|
|
2
2
|
import { a as visibilityOfKey, i as isValidStorageKey, n as ObjectNotFoundError, o as withVisibility, r as encodeKeyPath, t as InvalidStorageKeyError } from "./errors-nQvt9bs7.js";
|
|
3
3
|
import { t as assertNodeRuntime } from "./nodeRuntime-DzuiHUhY.js";
|
|
4
4
|
import { ShapioApiError, createClient, createDeliveryClient } from "@shapio/client";
|
|
@@ -15,7 +15,7 @@ import { mkdir, rename, rm, rmdir, stat, writeFile } from "node:fs/promises";
|
|
|
15
15
|
import { pipeline } from "node:stream/promises";
|
|
16
16
|
//#endregion
|
|
17
17
|
//#region ../../apps/api/src/constants/version.ts
|
|
18
|
-
const SHAPIO_VERSION$1 = "0.5.
|
|
18
|
+
const SHAPIO_VERSION$1 = "0.5.10";
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region ../../apps/api/src/helpers/errorResponse.ts
|
|
21
21
|
const STATUS_CODES = {
|
|
@@ -4310,7 +4310,7 @@ const guardingSkew = async (runtime, statement) => {
|
|
|
4310
4310
|
return await statement();
|
|
4311
4311
|
} catch (error) {
|
|
4312
4312
|
if (error instanceof AppError) throw error;
|
|
4313
|
-
const { getPendingMigrations } = await import("./migrator-
|
|
4313
|
+
const { getPendingMigrations } = await import("./migrator-BfVtncOo.js");
|
|
4314
4314
|
if ((await getPendingMigrations(runtime.db)).length > 0) throw new ShapioVersionSkewError$1(null, SHAPIO_VERSION$1, { cause: error });
|
|
4315
4315
|
throw error;
|
|
4316
4316
|
}
|
|
@@ -4744,7 +4744,7 @@ const groupByRole = (grants) => {
|
|
|
4744
4744
|
* Every role's grants, held in memory and reloaded when `system_versions.permissions_version` moves.
|
|
4745
4745
|
* The version is checked on every lookup: read here, or read once by the request (`versions`, see
|
|
4746
4746
|
* `KnownVersions`). That durable check is what makes a permission change visible to every instance from the
|
|
4747
|
-
* next request (NOTIFY would only be an
|
|
4747
|
+
* next request (NOTIFY would only be an optimization). Lookups on the pool share one reload; a
|
|
4748
4748
|
* lookup inside a transaction reads (and, when stale, reloads) through that transaction alone, never
|
|
4749
4749
|
* through a reload running on another request's transaction.
|
|
4750
4750
|
*/
|