@bubltec/mycota-media 0.2.0
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/fake.adapter.d.ts +14 -0
- package/dist/fake.adapter.d.ts.map +1 -0
- package/dist/fake.adapter.js +44 -0
- package/dist/fake.adapter.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/media.spec.d.ts +2 -0
- package/dist/media.spec.d.ts.map +1 -0
- package/dist/media.spec.js +45 -0
- package/dist/media.spec.js.map +1 -0
- package/dist/ports.d.ts +50 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +2 -0
- package/dist/ports.js.map +1 -0
- package/dist/s3.adapter.d.ts +23 -0
- package/dist/s3.adapter.d.ts.map +1 -0
- package/dist/s3.adapter.js +55 -0
- package/dist/s3.adapter.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MediaStore, ObjectBody, PutObjectInput, StoredObject } from './ports.js';
|
|
2
|
+
/**
|
|
3
|
+
* Process-local media for tests and local boot. URLs are `memory://` so
|
|
4
|
+
* nothing tries to fetch them over the network.
|
|
5
|
+
*/
|
|
6
|
+
export declare class FakeMediaStore implements MediaStore {
|
|
7
|
+
private readonly objects;
|
|
8
|
+
put(input: PutObjectInput): Promise<StoredObject>;
|
|
9
|
+
get(key: string): Promise<ObjectBody | undefined>;
|
|
10
|
+
delete(key: string): Promise<void>;
|
|
11
|
+
url(key: string, expiresInSeconds?: number): Promise<string>;
|
|
12
|
+
private memoryUrl;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=fake.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake.adapter.d.ts","sourceRoot":"","sources":["../src/fake.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AASvF;;;GAGG;AACH,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IAEjD,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC;IAiBjD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAMjD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAMhE,OAAO,CAAC,SAAS;CAIlB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-local media for tests and local boot. URLs are `memory://` so
|
|
3
|
+
* nothing tries to fetch them over the network.
|
|
4
|
+
*/
|
|
5
|
+
export class FakeMediaStore {
|
|
6
|
+
objects = new Map();
|
|
7
|
+
async put(input) {
|
|
8
|
+
if (!input.key)
|
|
9
|
+
throw new Error('media key is required');
|
|
10
|
+
this.objects.set(input.key, {
|
|
11
|
+
body: input.body,
|
|
12
|
+
contentType: input.contentType,
|
|
13
|
+
visibility: input.visibility,
|
|
14
|
+
cacheControl: input.cacheControl,
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
key: input.key,
|
|
18
|
+
url: this.memoryUrl(input.key, input.visibility),
|
|
19
|
+
contentType: input.contentType,
|
|
20
|
+
sizeBytes: input.body.byteLength,
|
|
21
|
+
visibility: input.visibility,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async get(key) {
|
|
25
|
+
const recorded = this.objects.get(key);
|
|
26
|
+
if (!recorded)
|
|
27
|
+
return undefined;
|
|
28
|
+
return { body: recorded.body, contentType: recorded.contentType };
|
|
29
|
+
}
|
|
30
|
+
async delete(key) {
|
|
31
|
+
this.objects.delete(key);
|
|
32
|
+
}
|
|
33
|
+
async url(key, expiresInSeconds = 3600) {
|
|
34
|
+
const recorded = this.objects.get(key);
|
|
35
|
+
if (!recorded)
|
|
36
|
+
throw new Error(`media ${key} not found`);
|
|
37
|
+
return this.memoryUrl(key, recorded.visibility, expiresInSeconds);
|
|
38
|
+
}
|
|
39
|
+
memoryUrl(key, visibility, expiresInSeconds) {
|
|
40
|
+
const exp = visibility === 'private' ? `?exp=${expiresInSeconds ?? 3600}` : '';
|
|
41
|
+
return `memory://${key}${exp}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=fake.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake.adapter.js","sourceRoot":"","sources":["../src/fake.adapter.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,OAAO,cAAc;IACR,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEvD,KAAK,CAAC,GAAG,CAAC,KAAqB;QAC7B,IAAI,CAAC,KAAK,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE;YAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,YAAY,EAAE,KAAK,CAAC,YAAY;SACjC,CAAC,CAAC;QACH,OAAO;YACL,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC;YAChD,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU;YAChC,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,gBAAgB,GAAG,IAAI;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,SAAS,GAAG,YAAY,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;IACpE,CAAC;IAEO,SAAS,CAAC,GAAW,EAAE,UAAwC,EAAE,gBAAyB;QAChG,MAAM,GAAG,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,gBAAgB,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,OAAO,YAAY,GAAG,GAAG,GAAG,EAAE,CAAC;IACjC,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { MediaStore, MediaVisibility, ObjectBody, ObjectStoreClient, PutObjectInput, StoredObject } from './ports.js';
|
|
2
|
+
export { FakeMediaStore } from './fake.adapter.js';
|
|
3
|
+
export { S3MediaStore, type S3MediaStoreOptions } from './s3.adapter.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3H,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAA4B,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.spec.d.ts","sourceRoot":"","sources":["../src/media.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { FakeMediaStore } from './fake.adapter.js';
|
|
3
|
+
import { S3MediaStore } from './index.js';
|
|
4
|
+
describe('FakeMediaStore', () => {
|
|
5
|
+
it('round-trips bytes and signs private URLs', async () => {
|
|
6
|
+
const store = new FakeMediaStore();
|
|
7
|
+
const body = new TextEncoder().encode('flyer');
|
|
8
|
+
const stored = await store.put({
|
|
9
|
+
key: 'events/e1/flyer.pdf',
|
|
10
|
+
body,
|
|
11
|
+
contentType: 'application/pdf',
|
|
12
|
+
visibility: 'private',
|
|
13
|
+
});
|
|
14
|
+
expect(stored.url).toBe('memory://events/e1/flyer.pdf?exp=3600');
|
|
15
|
+
expect((await store.get(stored.key))?.contentType).toBe('application/pdf');
|
|
16
|
+
await store.delete(stored.key);
|
|
17
|
+
expect(await store.get(stored.key)).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('S3MediaStore', () => {
|
|
21
|
+
it('uses the public base URL for public objects and signs private ones', async () => {
|
|
22
|
+
const client = {
|
|
23
|
+
putObject: vi.fn(async () => undefined),
|
|
24
|
+
getObject: vi.fn(async () => undefined),
|
|
25
|
+
deleteObject: vi.fn(async () => undefined),
|
|
26
|
+
getSignedUrl: vi.fn(async (key, exp) => `https://signed.local/${key}?e=${exp}`),
|
|
27
|
+
};
|
|
28
|
+
const store = new S3MediaStore(client, { publicBaseUrl: 'https://cdn.example.com/' });
|
|
29
|
+
const poster = await store.put({
|
|
30
|
+
key: 'posters/a.jpg',
|
|
31
|
+
body: new Uint8Array([1, 2, 3]),
|
|
32
|
+
contentType: 'image/jpeg',
|
|
33
|
+
visibility: 'public',
|
|
34
|
+
});
|
|
35
|
+
expect(poster.url).toBe('https://cdn.example.com/posters/a.jpg');
|
|
36
|
+
const privateShot = await store.put({
|
|
37
|
+
key: 'merch/sku.png',
|
|
38
|
+
body: new Uint8Array([4]),
|
|
39
|
+
contentType: 'image/png',
|
|
40
|
+
visibility: 'private',
|
|
41
|
+
});
|
|
42
|
+
expect(privateShot.url).toContain('signed.local');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=media.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.spec.js","sourceRoot":"","sources":["../src/media.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAA0B,MAAM,YAAY,CAAC;AAElE,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,KAAK,GAAG,IAAI,cAAc,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC;YAC7B,GAAG,EAAE,qBAAqB;YAC1B,IAAI;YACJ,WAAW,EAAE,iBAAiB;YAC9B,UAAU,EAAE,SAAS;SACtB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QACjE,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC3E,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,MAAM,GAAsB;YAChC,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;YACvC,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;YACvC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;YAC1C,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,wBAAwB,GAAG,MAAM,GAAG,EAAE,CAAC;SAChF,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACtF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC;YAC7B,GAAG,EAAE,eAAe;YACpB,IAAI,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/B,WAAW,EAAE,YAAY;YACzB,UAAU,EAAE,QAAQ;SACrB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QAEjE,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC;YAClC,GAAG,EAAE,eAAe;YACpB,IAAI,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YACzB,WAAW,EAAE,WAAW;YACxB,UAAU,EAAE,SAAS;SACtB,CAAC,CAAC;QACH,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type MediaVisibility = 'public' | 'private';
|
|
2
|
+
export interface PutObjectInput {
|
|
3
|
+
key: string;
|
|
4
|
+
body: Uint8Array;
|
|
5
|
+
contentType: string;
|
|
6
|
+
visibility: MediaVisibility;
|
|
7
|
+
cacheControl?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface StoredObject {
|
|
10
|
+
key: string;
|
|
11
|
+
/** Public URL, or a signed URL when the object is private. */
|
|
12
|
+
url: string;
|
|
13
|
+
contentType: string;
|
|
14
|
+
sizeBytes: number;
|
|
15
|
+
visibility: MediaVisibility;
|
|
16
|
+
}
|
|
17
|
+
export interface ObjectBody {
|
|
18
|
+
body: Uint8Array;
|
|
19
|
+
contentType: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Byte store for posters, Reels, merch shots, print PDFs. Inner layers never
|
|
23
|
+
* import S3 — they put a key and get a URL.
|
|
24
|
+
*/
|
|
25
|
+
export interface MediaStore {
|
|
26
|
+
put(input: PutObjectInput): Promise<StoredObject>;
|
|
27
|
+
get(key: string): Promise<ObjectBody | undefined>;
|
|
28
|
+
delete(key: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Public objects return the CDN/base URL. Private objects return a signed
|
|
31
|
+
* GET that expires. `expiresInSeconds` is ignored for public objects.
|
|
32
|
+
*/
|
|
33
|
+
url(key: string, expiresInSeconds?: number): Promise<string>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Narrow object-store surface so `@bubltec/mycota-media` does not take an AWS
|
|
37
|
+
* SDK dependency. The consuming app constructs S3 (or a test double).
|
|
38
|
+
*/
|
|
39
|
+
export interface ObjectStoreClient {
|
|
40
|
+
putObject(input: {
|
|
41
|
+
key: string;
|
|
42
|
+
body: Uint8Array;
|
|
43
|
+
contentType: string;
|
|
44
|
+
cacheControl?: string;
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
getObject(key: string): Promise<ObjectBody | undefined>;
|
|
47
|
+
deleteObject(key: string): Promise<void>;
|
|
48
|
+
getSignedUrl(key: string, expiresInSeconds: number): Promise<string>;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAClD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9D;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,KAAK,EAAE;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,UAAU,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACxD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACtE"}
|
package/dist/ports.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MediaStore, ObjectBody, ObjectStoreClient, PutObjectInput, StoredObject } from './ports.js';
|
|
2
|
+
export interface S3MediaStoreOptions {
|
|
3
|
+
/** e.g. https://media.example.com or https://bucket.s3.amazonaws.com */
|
|
4
|
+
publicBaseUrl: string;
|
|
5
|
+
defaultExpiresInSeconds?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* S3 (or S3-compatible) adapter. Public keys are concatenated onto
|
|
9
|
+
* `publicBaseUrl`; private keys go through the client's signed GET.
|
|
10
|
+
*/
|
|
11
|
+
export declare class S3MediaStore implements MediaStore {
|
|
12
|
+
private readonly client;
|
|
13
|
+
private readonly options;
|
|
14
|
+
private readonly publicKeys;
|
|
15
|
+
constructor(client: ObjectStoreClient, options: S3MediaStoreOptions);
|
|
16
|
+
put(input: PutObjectInput): Promise<StoredObject>;
|
|
17
|
+
get(key: string): Promise<ObjectBody | undefined>;
|
|
18
|
+
delete(key: string): Promise<void>;
|
|
19
|
+
url(key: string, expiresInSeconds?: number): Promise<string>;
|
|
20
|
+
private publicUrl;
|
|
21
|
+
private urlFor;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=s3.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.adapter.d.ts","sourceRoot":"","sources":["../src/s3.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1G,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,UAAU;IAI3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;gBAG7B,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,mBAAmB;IAGzC,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBjD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAIjD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlE,OAAO,CAAC,SAAS;YAIH,MAAM;CAIrB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3 (or S3-compatible) adapter. Public keys are concatenated onto
|
|
3
|
+
* `publicBaseUrl`; private keys go through the client's signed GET.
|
|
4
|
+
*/
|
|
5
|
+
export class S3MediaStore {
|
|
6
|
+
client;
|
|
7
|
+
options;
|
|
8
|
+
publicKeys = new Set();
|
|
9
|
+
constructor(client, options) {
|
|
10
|
+
this.client = client;
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
async put(input) {
|
|
14
|
+
if (!input.key)
|
|
15
|
+
throw new Error('media key is required');
|
|
16
|
+
await this.client.putObject({
|
|
17
|
+
key: input.key,
|
|
18
|
+
body: input.body,
|
|
19
|
+
contentType: input.contentType,
|
|
20
|
+
cacheControl: input.cacheControl,
|
|
21
|
+
});
|
|
22
|
+
if (input.visibility === 'public')
|
|
23
|
+
this.publicKeys.add(input.key);
|
|
24
|
+
else
|
|
25
|
+
this.publicKeys.delete(input.key);
|
|
26
|
+
return {
|
|
27
|
+
key: input.key,
|
|
28
|
+
url: await this.urlFor(input.key, input.visibility),
|
|
29
|
+
contentType: input.contentType,
|
|
30
|
+
sizeBytes: input.body.byteLength,
|
|
31
|
+
visibility: input.visibility,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async get(key) {
|
|
35
|
+
return this.client.getObject(key);
|
|
36
|
+
}
|
|
37
|
+
async delete(key) {
|
|
38
|
+
this.publicKeys.delete(key);
|
|
39
|
+
await this.client.deleteObject(key);
|
|
40
|
+
}
|
|
41
|
+
async url(key, expiresInSeconds) {
|
|
42
|
+
if (this.publicKeys.has(key))
|
|
43
|
+
return this.publicUrl(key);
|
|
44
|
+
return this.client.getSignedUrl(key, expiresInSeconds ?? this.options.defaultExpiresInSeconds ?? 3600);
|
|
45
|
+
}
|
|
46
|
+
publicUrl(key) {
|
|
47
|
+
return `${this.options.publicBaseUrl.replace(/\/$/, '')}/${key.replace(/^\//, '')}`;
|
|
48
|
+
}
|
|
49
|
+
async urlFor(key, visibility) {
|
|
50
|
+
if (visibility === 'public')
|
|
51
|
+
return this.publicUrl(key);
|
|
52
|
+
return this.url(key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=s3.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.adapter.js","sourceRoot":"","sources":["../src/s3.adapter.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,OAAO,YAAY;IAIJ;IACA;IAJF,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhD,YACmB,MAAyB,EACzB,OAA4B;QAD5B,WAAM,GAAN,MAAM,CAAmB;QACzB,YAAO,GAAP,OAAO,CAAqB;IAC5C,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,KAAqB;QAC7B,IAAI,CAAC,KAAK,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;YAC1B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,YAAY,EAAE,KAAK,CAAC,YAAY;SACjC,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,UAAU,KAAK,QAAQ;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;;YAC7D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO;YACL,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,GAAG,EAAE,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC;YACnD,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU;YAChC,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,gBAAyB;QAC9C,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,gBAAgB,IAAI,IAAI,CAAC,OAAO,CAAC,uBAAuB,IAAI,IAAI,CAAC,CAAC;IACzG,CAAC;IAEO,SAAS,CAAC,GAAW;QAC3B,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;IACtF,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,UAAwC;QACxE,IAAI,UAAU,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bubltec/mycota-media",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -b",
|
|
21
|
+
"typecheck": "tsc -b --noEmit",
|
|
22
|
+
"lint": "oxlint -c ../../.oxlintrc.json src",
|
|
23
|
+
"test": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.10.0",
|
|
27
|
+
"typescript": "^5.8.0",
|
|
28
|
+
"vitest": "^2.1.0"
|
|
29
|
+
}
|
|
30
|
+
}
|