@oino-ts/blob 1.0.5 → 1.0.6
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/package.json +4 -4
- package/src/OINOBlob.ts +18 -0
- package/src/OINOBlobApi.test.ts +2 -0
- package/src/OINOBlobApi.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oino-ts/blob",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "OINO TS library package for publishing blob storage as a REST API.",
|
|
5
5
|
"author": "Matias Kiviniemi (pragmatta)",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"module": "./dist/esm/index.js",
|
|
20
20
|
"types": "./dist/types/index.d.ts",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@oino-ts/common": "1.0.
|
|
22
|
+
"@oino-ts/common": "1.0.6",
|
|
23
23
|
"oino-ts": "file:.."
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@oino-ts/types": "1.0.
|
|
26
|
+
"@oino-ts/types": "1.0.6",
|
|
27
27
|
"@types/bun": "^1.1.14",
|
|
28
|
-
"@types/node": "^21.0.
|
|
28
|
+
"@types/node": "^21.0.60",
|
|
29
29
|
"typescript": "~5.9.0"
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
package/src/OINOBlob.ts
CHANGED
|
@@ -11,6 +11,8 @@ const BLOB_LIKE_ESCAPE_REGEX = /[.*+?^${}()|[\]\\]/g
|
|
|
11
11
|
const BLOB_LIKE_PERCENT_REGEX = /%/g
|
|
12
12
|
const BLOB_LIKE_UNDERSCORE_REGEX = /_/g
|
|
13
13
|
|
|
14
|
+
const BLOB_SANITIZE_DEFAULT_REGEX = /[\x00-\x1f\x7f]/g
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* Abstract base class for blob storage backends. Subclasses implement
|
|
16
18
|
* the two core operations (`listEntries` and `fetchEntry`) for a specific
|
|
@@ -62,6 +64,22 @@ export abstract class OINOBlob extends OINODataSource {
|
|
|
62
64
|
return v
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
// ── Blob name sanitization ──────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Sanitize a blob name by replacing characters that are illegal or unsafe
|
|
71
|
+
* on this storage backend with `_`.
|
|
72
|
+
*
|
|
73
|
+
* The base implementation strips ASCII control characters (U+0000–U+001F
|
|
74
|
+
* and U+007F). Subclasses should override to apply additional
|
|
75
|
+
* platform-specific rules.
|
|
76
|
+
*
|
|
77
|
+
* @param name raw blob name (path within the container)
|
|
78
|
+
*/
|
|
79
|
+
sanitizeName(name: string): string {
|
|
80
|
+
return name.replace(BLOB_SANITIZE_DEFAULT_REGEX, "_")
|
|
81
|
+
}
|
|
82
|
+
|
|
65
83
|
// ── Blob-specific filter helper ───────────────────────────────────────
|
|
66
84
|
|
|
67
85
|
/**
|
package/src/OINOBlobApi.test.ts
CHANGED
|
@@ -286,6 +286,7 @@ export async function OINOTestBlob(storageParams: OINOBlobStorageParams, testPar
|
|
|
286
286
|
expect(verify_result.success).toBe(true)
|
|
287
287
|
expect(verify_result.blobData).toBeDefined()
|
|
288
288
|
expect(new TextDecoder().decode(verify_result.blobData)).toBe(new TextDecoder().decode(testParams.uploadContent))
|
|
289
|
+
expect(verify_result.blobDataType).toBe(testParams.uploadContentType)
|
|
289
290
|
})
|
|
290
291
|
|
|
291
292
|
// ── UPDATE (PUT) ──────────────────────────────────────────────────────
|
|
@@ -326,6 +327,7 @@ export async function OINOTestBlob(storageParams: OINOBlobStorageParams, testPar
|
|
|
326
327
|
const verify_result: OINOBlobApiResult = await api.doApiRequest(verify_request)
|
|
327
328
|
expect(verify_result.success).toBe(true)
|
|
328
329
|
expect(new TextDecoder().decode(verify_result.blobData)).toBe(new TextDecoder().decode(testParams.updateContent))
|
|
330
|
+
expect(verify_result.blobDataType).toBe(testParams.uploadContentType)
|
|
329
331
|
})
|
|
330
332
|
|
|
331
333
|
// ── DELETE ────────────────────────────────────────────────────────────
|
package/src/OINOBlobApi.ts
CHANGED
|
@@ -126,7 +126,7 @@ export class OINOBlobApi extends OINOApi {
|
|
|
126
126
|
} else {
|
|
127
127
|
// ── Download blob ────────────────────────────────────────────
|
|
128
128
|
try {
|
|
129
|
-
const name = decodeURIComponent(request.rowId)
|
|
129
|
+
const name = this.blob.sanitizeName(decodeURIComponent(request.rowId))
|
|
130
130
|
const fetch_result = await this.blob.fetchEntry(name)
|
|
131
131
|
result.blobData = fetch_result.content
|
|
132
132
|
result.blobDataType = fetch_result.contentType
|
|
@@ -142,7 +142,7 @@ export class OINOBlobApi extends OINOApi {
|
|
|
142
142
|
result.setError(400, "HTTP " + request.method + " method requires an URL ID (blob name)!", "DoRequest")
|
|
143
143
|
} else {
|
|
144
144
|
try {
|
|
145
|
-
const name = decodeURIComponent(request.rowId)
|
|
145
|
+
const name = this.blob.sanitizeName(decodeURIComponent(request.rowId))
|
|
146
146
|
const content_type = request.headers.get("content-type") ?? "application/octet-stream"
|
|
147
147
|
const data = request.rowData
|
|
148
148
|
const content: Uint8Array = data instanceof Uint8Array ? data : request.bodyAsBuffer()
|
|
@@ -159,7 +159,7 @@ export class OINOBlobApi extends OINOApi {
|
|
|
159
159
|
result.setError(400, "HTTP DELETE method requires an URL ID (blob name)!", "DoRequest")
|
|
160
160
|
} else {
|
|
161
161
|
try {
|
|
162
|
-
const name = decodeURIComponent(request.rowId)
|
|
162
|
+
const name = this.blob.sanitizeName(decodeURIComponent(request.rowId))
|
|
163
163
|
await this.blob.deleteEntry(name)
|
|
164
164
|
} catch (e: any) {
|
|
165
165
|
result.setError(500, "Error deleting blob: " + e.message, "DoDelete")
|