@adonis-agora/media 0.1.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/dist/configure.d.ts +13 -0
  3. package/dist/configure.d.ts.map +1 -0
  4. package/dist/configure.js +20 -0
  5. package/dist/configure.js.map +1 -0
  6. package/dist/providers/media_provider.d.ts +20 -0
  7. package/dist/providers/media_provider.d.ts.map +1 -0
  8. package/dist/providers/media_provider.js +71 -0
  9. package/dist/providers/media_provider.js.map +1 -0
  10. package/dist/src/attachment.d.ts +83 -0
  11. package/dist/src/attachment.d.ts.map +1 -0
  12. package/dist/src/attachment.js +144 -0
  13. package/dist/src/attachment.js.map +1 -0
  14. package/dist/src/contents.d.ts +9 -0
  15. package/dist/src/contents.d.ts.map +1 -0
  16. package/dist/src/contents.js +17 -0
  17. package/dist/src/contents.js.map +1 -0
  18. package/dist/src/define_config.d.ts +58 -0
  19. package/dist/src/define_config.d.ts.map +1 -0
  20. package/dist/src/define_config.js +8 -0
  21. package/dist/src/define_config.js.map +1 -0
  22. package/dist/src/diagnostics.d.ts +49 -0
  23. package/dist/src/diagnostics.d.ts.map +1 -0
  24. package/dist/src/diagnostics.js +24 -0
  25. package/dist/src/diagnostics.js.map +1 -0
  26. package/dist/src/errors.d.ts +17 -0
  27. package/dist/src/errors.d.ts.map +1 -0
  28. package/dist/src/errors.js +29 -0
  29. package/dist/src/errors.js.map +1 -0
  30. package/dist/src/image_processor.d.ts +21 -0
  31. package/dist/src/image_processor.d.ts.map +1 -0
  32. package/dist/src/image_processor.js +2 -0
  33. package/dist/src/image_processor.js.map +1 -0
  34. package/dist/src/index.d.ts +20 -0
  35. package/dist/src/index.d.ts.map +1 -0
  36. package/dist/src/index.js +13 -0
  37. package/dist/src/index.js.map +1 -0
  38. package/dist/src/media_collection.d.ts +19 -0
  39. package/dist/src/media_collection.d.ts.map +1 -0
  40. package/dist/src/media_collection.js +12 -0
  41. package/dist/src/media_collection.js.map +1 -0
  42. package/dist/src/media_library.d.ts +71 -0
  43. package/dist/src/media_library.d.ts.map +1 -0
  44. package/dist/src/media_library.js +176 -0
  45. package/dist/src/media_library.js.map +1 -0
  46. package/dist/src/media_manager.d.ts +38 -0
  47. package/dist/src/media_manager.d.ts.map +1 -0
  48. package/dist/src/media_manager.js +46 -0
  49. package/dist/src/media_manager.js.map +1 -0
  50. package/dist/src/media_record.d.ts +25 -0
  51. package/dist/src/media_record.d.ts.map +1 -0
  52. package/dist/src/media_record.js +2 -0
  53. package/dist/src/media_record.js.map +1 -0
  54. package/dist/src/media_store.d.ts +16 -0
  55. package/dist/src/media_store.d.ts.map +1 -0
  56. package/dist/src/media_store.js +2 -0
  57. package/dist/src/media_store.js.map +1 -0
  58. package/dist/src/processors/factory.d.ts +22 -0
  59. package/dist/src/processors/factory.d.ts.map +1 -0
  60. package/dist/src/processors/factory.js +21 -0
  61. package/dist/src/processors/factory.js.map +1 -0
  62. package/dist/src/processors/sharp.d.ts +10 -0
  63. package/dist/src/processors/sharp.d.ts.map +1 -0
  64. package/dist/src/processors/sharp.js +29 -0
  65. package/dist/src/processors/sharp.js.map +1 -0
  66. package/dist/src/storage_manager.d.ts +19 -0
  67. package/dist/src/storage_manager.d.ts.map +1 -0
  68. package/dist/src/storage_manager.js +17 -0
  69. package/dist/src/storage_manager.js.map +1 -0
  70. package/dist/src/stores/factory.d.ts +51 -0
  71. package/dist/src/stores/factory.d.ts.map +1 -0
  72. package/dist/src/stores/factory.js +39 -0
  73. package/dist/src/stores/factory.js.map +1 -0
  74. package/dist/src/stores/lucid.d.ts +29 -0
  75. package/dist/src/stores/lucid.d.ts.map +1 -0
  76. package/dist/src/stores/lucid.js +106 -0
  77. package/dist/src/stores/lucid.js.map +1 -0
  78. package/dist/src/testing/fake_image_processor.d.ts +14 -0
  79. package/dist/src/testing/fake_image_processor.d.ts.map +1 -0
  80. package/dist/src/testing/fake_image_processor.js +21 -0
  81. package/dist/src/testing/fake_image_processor.js.map +1 -0
  82. package/dist/src/testing/in_memory_disk.d.ts +44 -0
  83. package/dist/src/testing/in_memory_disk.d.ts.map +1 -0
  84. package/dist/src/testing/in_memory_disk.js +80 -0
  85. package/dist/src/testing/in_memory_disk.js.map +1 -0
  86. package/dist/src/testing/in_memory_media_store.d.ts +12 -0
  87. package/dist/src/testing/in_memory_media_store.d.ts.map +1 -0
  88. package/dist/src/testing/in_memory_media_store.js +29 -0
  89. package/dist/src/testing/in_memory_media_store.js.map +1 -0
  90. package/dist/src/testing/index.d.ts +4 -0
  91. package/dist/src/testing/index.d.ts.map +1 -0
  92. package/dist/src/testing/index.js +4 -0
  93. package/dist/src/testing/index.js.map +1 -0
  94. package/dist/src/types.d.ts +44 -0
  95. package/dist/src/types.d.ts.map +1 -0
  96. package/dist/src/types.js +2 -0
  97. package/dist/src/types.js.map +1 -0
  98. package/dist/stubs/config/media.stub +36 -0
  99. package/dist/stubs/database/migrations/create_media_table.stub +39 -0
  100. package/dist/stubs/main.d.ts +3 -0
  101. package/dist/stubs/main.d.ts.map +1 -0
  102. package/dist/stubs/main.js +4 -0
  103. package/dist/stubs/main.js.map +1 -0
  104. package/package.json +106 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The image-processor factory namespace used in `config/media.ts`:
3
+ *
4
+ * ```ts
5
+ * import { defineConfig, processors } from '@adonis-agora/media'
6
+ *
7
+ * export default defineConfig({
8
+ * imageProcessor: processors.sharp(),
9
+ * })
10
+ * ```
11
+ */
12
+ export const processors = {
13
+ /** sharp-backed resize/crop/format/quality (needs the optional `sharp` peer). */
14
+ sharp() {
15
+ return async () => {
16
+ const { SharpImageProcessor } = await import('./sharp.js');
17
+ return new SharpImageProcessor();
18
+ };
19
+ },
20
+ };
21
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/processors/factory.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,iFAAiF;IACjF,KAAK;QACH,OAAO,KAAK,IAAI,EAAE;YAChB,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YAC3D,OAAO,IAAI,mBAAmB,EAAE,CAAC;QACnC,CAAC,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ConversionPreset, ConversionResult, ImageProcessor } from '../image_processor.js';
2
+ /**
3
+ * `ImageProcessor` backed by [sharp](https://sharp.pixelplumbing.com). Resize/crop via `fit`,
4
+ * convert format, and apply quality. The default output format is `webp`. Import this directly
5
+ * (`@adonis-agora/media/processors/sharp`) or select it lazily with `processors.sharp()` in config.
6
+ */
7
+ export declare class SharpImageProcessor implements ImageProcessor {
8
+ convert(input: Buffer, preset: ConversionPreset): Promise<ConversionResult>;
9
+ }
10
+ //# sourceMappingURL=sharp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharp.d.ts","sourceRoot":"","sources":["../../../src/processors/sharp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAShG;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IAClD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAiBlF"}
@@ -0,0 +1,29 @@
1
+ import sharp from 'sharp';
2
+ const CONTENT_TYPE = {
3
+ jpeg: 'image/jpeg',
4
+ png: 'image/png',
5
+ webp: 'image/webp',
6
+ avif: 'image/avif',
7
+ };
8
+ /**
9
+ * `ImageProcessor` backed by [sharp](https://sharp.pixelplumbing.com). Resize/crop via `fit`,
10
+ * convert format, and apply quality. The default output format is `webp`. Import this directly
11
+ * (`@adonis-agora/media/processors/sharp`) or select it lazily with `processors.sharp()` in config.
12
+ */
13
+ export class SharpImageProcessor {
14
+ async convert(input, preset) {
15
+ let pipeline = sharp(input);
16
+ if (preset.width || preset.height) {
17
+ pipeline = pipeline.resize({
18
+ ...(preset.width ? { width: preset.width } : {}),
19
+ ...(preset.height ? { height: preset.height } : {}),
20
+ fit: preset.fit ?? 'cover',
21
+ });
22
+ }
23
+ const format = preset.format ?? 'webp';
24
+ pipeline = pipeline.toFormat(format, preset.quality ? { quality: preset.quality } : {});
25
+ const data = await pipeline.toBuffer();
26
+ return { data, format, contentType: CONTENT_TYPE[format] ?? 'application/octet-stream' };
27
+ }
28
+ }
29
+ //# sourceMappingURL=sharp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharp.js","sourceRoot":"","sources":["../../../src/processors/sharp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,YAAY,GAA2B;IAC3C,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,mBAAmB;IAC9B,KAAK,CAAC,OAAO,CAAC,KAAa,EAAE,MAAwB;QACnD,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAE5B,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC;gBACzB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChD,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnD,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,OAAO;aAC3B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC;QACvC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAExF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,IAAI,0BAA0B,EAAE,CAAC;IAC3F,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import type { Disk, DiskResolver } from './types.js';
2
+ export interface StorageManagerOptions {
3
+ /** Name of the default disk (must be resolvable by the resolver). */
4
+ default: string;
5
+ /** Resolves a disk by name; in production `(name) => drive.use(name)`. */
6
+ resolve: DiskResolver;
7
+ }
8
+ /**
9
+ * Thin façade over a {@link DiskResolver} (an `@adonisjs/drive` manager in production).
10
+ * The media-library and attachment layers talk to disks only through this, so they never
11
+ * import Drive and the in-memory test resolver is a drop-in.
12
+ */
13
+ export declare class StorageManager {
14
+ readonly defaultDisk: string;
15
+ private readonly resolver;
16
+ constructor(options: StorageManagerOptions);
17
+ disk(name?: string): Disk;
18
+ }
19
+ //# sourceMappingURL=storage_manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage_manager.d.ts","sourceRoot":"","sources":["../../src/storage_manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;gBAE5B,OAAO,EAAE,qBAAqB;IAK1C,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;CAG1B"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Thin façade over a {@link DiskResolver} (an `@adonisjs/drive` manager in production).
3
+ * The media-library and attachment layers talk to disks only through this, so they never
4
+ * import Drive and the in-memory test resolver is a drop-in.
5
+ */
6
+ export class StorageManager {
7
+ defaultDisk;
8
+ resolver;
9
+ constructor(options) {
10
+ this.defaultDisk = options.default;
11
+ this.resolver = options.resolve;
12
+ }
13
+ disk(name) {
14
+ return this.resolver(name ?? this.defaultDisk);
15
+ }
16
+ }
17
+ //# sourceMappingURL=storage_manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage_manager.js","sourceRoot":"","sources":["../../src/storage_manager.ts"],"names":[],"mappings":"AASA;;;;GAIG;AACH,MAAM,OAAO,cAAc;IAChB,WAAW,CAAS;IACZ,QAAQ,CAAe;IAExC,YAAY,OAA8B;QACxC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,IAAa;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC;IACjD,CAAC;CACF"}
@@ -0,0 +1,51 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ import type { MediaStore } from '../media_store.js';
3
+ /**
4
+ * Runtime context a {@link StoreFactory} thunk receives when the media provider builds the
5
+ * configured store at boot. Carries the booted application so a driver can resolve a peer's
6
+ * service (the Lucid `db`) if it needs to.
7
+ */
8
+ export interface StoreContext {
9
+ app: ApplicationService;
10
+ }
11
+ /**
12
+ * A configured media store: a thunk the media provider calls at boot to build the {@link MediaStore}.
13
+ * Each factory lazily imports its peer dependency (`@adonisjs/lucid`) inside the thunk, so the driver
14
+ * is only loaded when it is actually selected — keeping that package optional.
15
+ */
16
+ export type StoreFactory = (ctx: StoreContext) => Promise<MediaStore>;
17
+ /** Options for the Lucid-backed media store. */
18
+ export interface LucidStoreConfig {
19
+ /** Lucid connection name to use. Defaults to the `Database` default connection. */
20
+ connection?: string;
21
+ /** Table name. Default `media`. */
22
+ table?: string;
23
+ }
24
+ /** Options for the in-memory media store (single-process, non-durable). */
25
+ export interface MemoryStoreConfig {
26
+ }
27
+ /**
28
+ * The store factory namespace used in `config/media.ts`:
29
+ *
30
+ * ```ts
31
+ * import { defineConfig, stores } from '@adonis-agora/media'
32
+ *
33
+ * export default defineConfig({
34
+ * store: 'lucid',
35
+ * stores: {
36
+ * memory: stores.memory(),
37
+ * lucid: stores.lucid({ connection: 'pg' }),
38
+ * },
39
+ * })
40
+ * ```
41
+ *
42
+ * Each factory returns a {@link StoreFactory} — a lazy thunk. Calling it in the config file costs
43
+ * nothing; the peer dependency is only imported when the provider builds the selected store at boot.
44
+ */
45
+ export declare const stores: {
46
+ /** In-memory store — single-process, non-durable. Handy for tests and scratch apps. */
47
+ memory(_config?: MemoryStoreConfig): StoreFactory;
48
+ /** Persist media records in SQL via `@adonisjs/lucid`. */
49
+ lucid(config?: LucidStoreConfig): StoreFactory;
50
+ };
51
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/stores/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,kBAAkB,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtE,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;CAEjC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,MAAM;IACjB,uFAAuF;qBACvE,iBAAiB,GAAQ,YAAY;IAOrD,0DAA0D;mBAC5C,gBAAgB,GAAQ,YAAY;CAUnD,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The store factory namespace used in `config/media.ts`:
3
+ *
4
+ * ```ts
5
+ * import { defineConfig, stores } from '@adonis-agora/media'
6
+ *
7
+ * export default defineConfig({
8
+ * store: 'lucid',
9
+ * stores: {
10
+ * memory: stores.memory(),
11
+ * lucid: stores.lucid({ connection: 'pg' }),
12
+ * },
13
+ * })
14
+ * ```
15
+ *
16
+ * Each factory returns a {@link StoreFactory} — a lazy thunk. Calling it in the config file costs
17
+ * nothing; the peer dependency is only imported when the provider builds the selected store at boot.
18
+ */
19
+ export const stores = {
20
+ /** In-memory store — single-process, non-durable. Handy for tests and scratch apps. */
21
+ memory(_config = {}) {
22
+ return async () => {
23
+ const { InMemoryMediaStore } = await import('../testing/in_memory_media_store.js');
24
+ return new InMemoryMediaStore();
25
+ };
26
+ },
27
+ /** Persist media records in SQL via `@adonisjs/lucid`. */
28
+ lucid(config = {}) {
29
+ return async () => {
30
+ const db = (await import('@adonisjs/lucid/services/db')).default;
31
+ const { LucidMediaStore } = await import('./lucid.js');
32
+ return new LucidMediaStore(db, {
33
+ ...(config.connection !== undefined ? { connectionName: config.connection } : {}),
34
+ ...(config.table !== undefined ? { table: config.table } : {}),
35
+ });
36
+ };
37
+ },
38
+ };
39
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/stores/factory.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,uFAAuF;IACvF,MAAM,CAAC,UAA6B,EAAE;QACpC,OAAO,KAAK,IAAI,EAAE;YAChB,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,qCAAqC,CAAC,CAAC;YACnF,OAAO,IAAI,kBAAkB,EAAE,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;IAED,0DAA0D;IAC1D,KAAK,CAAC,SAA2B,EAAE;QACjC,OAAO,KAAK,IAAI,EAAE;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC;YACjE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YACvD,OAAO,IAAI,eAAe,CAAC,EAAE,EAAE;gBAC7B,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC/D,CAAC,CAAC;QACL,CAAC,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { Database } from '@adonisjs/lucid/database';
2
+ import type { MediaRecord } from '../media_record.js';
3
+ import type { MediaStore } from '../media_store.js';
4
+ export interface LucidMediaStoreOptions {
5
+ /** The Lucid connection name. Defaults to the `Database` default connection. */
6
+ connectionName?: string;
7
+ /** Table name. Default `media`. */
8
+ table?: string;
9
+ }
10
+ /**
11
+ * Lucid-backed {@link MediaStore}. Persists media records in a `media` table via `@adonisjs/lucid`'s
12
+ * query builder (Knex), so it works across SQLite / Postgres / MySQL. JSON payloads
13
+ * (`custom_properties`, `conversions`) are stored as TEXT and timestamps as epoch-ms integers, so the
14
+ * schema is portable across engines. Publish the migration with `node ace configure @adonis-agora/media`.
15
+ */
16
+ export declare class LucidMediaStore implements MediaStore {
17
+ private readonly db;
18
+ private readonly table;
19
+ private readonly connectionName;
20
+ constructor(db: Database, options?: LucidMediaStoreOptions);
21
+ private client;
22
+ private query;
23
+ save(record: MediaRecord): Promise<MediaRecord>;
24
+ find(id: string): Promise<MediaRecord | null>;
25
+ listByOwner(ownerType: string, ownerId: string, collection?: string): Promise<MediaRecord[]>;
26
+ delete(id: string): Promise<void>;
27
+ nextOrder(ownerType: string, ownerId: string, collection: string): Promise<number>;
28
+ }
29
+ //# sourceMappingURL=lucid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lucid.d.ts","sourceRoot":"","sources":["../../../src/stores/lucid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,sBAAsB;IACrC,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAK9C,OAAO,CAAC,QAAQ,CAAC,EAAE;IAJrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;gBAGjC,EAAE,EAAE,QAAQ,EAC7B,OAAO,GAAE,sBAA2B;IAMtC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,KAAK;IAIP,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAW/C,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAK7C,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,EAAE,CAAC;IAOnB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAUzF"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Lucid-backed {@link MediaStore}. Persists media records in a `media` table via `@adonisjs/lucid`'s
3
+ * query builder (Knex), so it works across SQLite / Postgres / MySQL. JSON payloads
4
+ * (`custom_properties`, `conversions`) are stored as TEXT and timestamps as epoch-ms integers, so the
5
+ * schema is portable across engines. Publish the migration with `node ace configure @adonis-agora/media`.
6
+ */
7
+ export class LucidMediaStore {
8
+ db;
9
+ table;
10
+ connectionName;
11
+ constructor(db, options = {}) {
12
+ this.db = db;
13
+ this.table = options.table ?? 'media';
14
+ this.connectionName = options.connectionName;
15
+ }
16
+ client() {
17
+ return this.connectionName ? this.db.connection(this.connectionName) : this.db.connection();
18
+ }
19
+ query() {
20
+ return this.client().from(this.table);
21
+ }
22
+ async save(record) {
23
+ const row = toRow(record);
24
+ const exists = await this.query().where('id', record.id).first();
25
+ if (exists) {
26
+ await this.query().where('id', record.id).update(row);
27
+ }
28
+ else {
29
+ await this.client().table(this.table).insert(row);
30
+ }
31
+ return { ...record };
32
+ }
33
+ async find(id) {
34
+ const row = (await this.query().where('id', id).first());
35
+ return row ? fromRow(row) : null;
36
+ }
37
+ async listByOwner(ownerType, ownerId, collection) {
38
+ let q = this.query().where('owner_type', ownerType).where('owner_id', ownerId);
39
+ if (collection !== undefined)
40
+ q = q.where('collection', collection);
41
+ const rows = (await q.orderBy('order', 'asc'));
42
+ return rows.map(fromRow);
43
+ }
44
+ async delete(id) {
45
+ await this.query().where('id', id).delete();
46
+ }
47
+ async nextOrder(ownerType, ownerId, collection) {
48
+ const row = (await this.query()
49
+ .where('owner_type', ownerType)
50
+ .where('owner_id', ownerId)
51
+ .where('collection', collection)
52
+ .max('order as max_order')
53
+ .first());
54
+ const max = row?.max_order;
55
+ return max === null || max === undefined ? 0 : Number(max) + 1;
56
+ }
57
+ }
58
+ function toRow(record) {
59
+ return {
60
+ id: record.id,
61
+ owner_type: record.ownerType,
62
+ owner_id: record.ownerId,
63
+ collection: record.collection,
64
+ name: record.name,
65
+ file_name: record.fileName,
66
+ mime_type: record.mimeType,
67
+ size: record.size,
68
+ disk: record.disk,
69
+ path: record.path,
70
+ order: record.order,
71
+ custom_properties: JSON.stringify(record.customProperties ?? {}),
72
+ conversions: JSON.stringify(record.conversions ?? {}),
73
+ created_at: record.createdAt.getTime(),
74
+ updated_at: record.updatedAt.getTime(),
75
+ };
76
+ }
77
+ function fromRow(row) {
78
+ return {
79
+ id: row.id,
80
+ ownerType: row.owner_type,
81
+ ownerId: row.owner_id,
82
+ collection: row.collection,
83
+ name: row.name,
84
+ fileName: row.file_name,
85
+ mimeType: row.mime_type,
86
+ size: Number(row.size),
87
+ disk: row.disk,
88
+ path: row.path,
89
+ order: Number(row.order),
90
+ customProperties: parseJson(row.custom_properties, {}),
91
+ conversions: parseJson(row.conversions, {}),
92
+ createdAt: new Date(Number(row.created_at)),
93
+ updatedAt: new Date(Number(row.updated_at)),
94
+ };
95
+ }
96
+ function parseJson(value, fallback) {
97
+ if (!value)
98
+ return fallback;
99
+ try {
100
+ return JSON.parse(value);
101
+ }
102
+ catch {
103
+ return fallback;
104
+ }
105
+ }
106
+ //# sourceMappingURL=lucid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lucid.js","sourceRoot":"","sources":["../../../src/stores/lucid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ConversionPreset, ConversionResult, ImageProcessor } from '../image_processor.js';
2
+ /**
3
+ * Deterministic {@link ImageProcessor} for tests — never touches sharp. It records every call and
4
+ * returns a tiny synthetic buffer tagged with the preset, so conversion paths and lazy/eager
5
+ * behaviour can be asserted without real image decoding.
6
+ */
7
+ export declare class FakeImageProcessor implements ImageProcessor {
8
+ readonly calls: Array<{
9
+ inputSize: number;
10
+ preset: ConversionPreset;
11
+ }>;
12
+ convert(input: Buffer, preset: ConversionPreset): Promise<ConversionResult>;
13
+ }
14
+ //# sourceMappingURL=fake_image_processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake_image_processor.d.ts","sourceRoot":"","sources":["../../../src/testing/fake_image_processor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAShG;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAM;IAEtE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAMlF"}
@@ -0,0 +1,21 @@
1
+ const CONTENT_TYPE = {
2
+ jpeg: 'image/jpeg',
3
+ png: 'image/png',
4
+ webp: 'image/webp',
5
+ avif: 'image/avif',
6
+ };
7
+ /**
8
+ * Deterministic {@link ImageProcessor} for tests — never touches sharp. It records every call and
9
+ * returns a tiny synthetic buffer tagged with the preset, so conversion paths and lazy/eager
10
+ * behaviour can be asserted without real image decoding.
11
+ */
12
+ export class FakeImageProcessor {
13
+ calls = [];
14
+ async convert(input, preset) {
15
+ this.calls.push({ inputSize: input.byteLength, preset });
16
+ const format = preset.format ?? 'webp';
17
+ const data = Buffer.from(`fake:${preset.name}:${preset.width ?? ''}x${preset.height ?? ''}`);
18
+ return { data, format, contentType: CONTENT_TYPE[format] ?? 'application/octet-stream' };
19
+ }
20
+ }
21
+ //# sourceMappingURL=fake_image_processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake_image_processor.js","sourceRoot":"","sources":["../../../src/testing/fake_image_processor.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAA2B;IAC3C,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IACpB,KAAK,GAA2D,EAAE,CAAC;IAE5E,KAAK,CAAC,OAAO,CAAC,KAAa,EAAE,MAAwB;QACnD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7F,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,MAAM,CAAC,IAAI,0BAA0B,EAAE,CAAC;IAC3F,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ import { Readable } from 'node:stream';
2
+ import type { Disk, DiskMetaData, DiskResolver, DiskWriteOptions, SignedUrlOptions } from '../types.js';
3
+ interface StoredFile {
4
+ data: Buffer;
5
+ contentType?: string | undefined;
6
+ lastModified: Date;
7
+ }
8
+ /**
9
+ * In-memory {@link Disk} satisfying the same structural contract as an `@adonisjs/drive` disk.
10
+ * Use it (via {@link inMemoryDiskResolver}) to drive the media-library and attachment layers in
11
+ * tests without a real filesystem or S3.
12
+ */
13
+ export declare class InMemoryDisk implements Disk {
14
+ /** Used to render `getUrl`/`getSignedUrl`. */
15
+ private readonly baseUrl;
16
+ readonly files: Map<string, StoredFile>;
17
+ constructor(
18
+ /** Used to render `getUrl`/`getSignedUrl`. */
19
+ baseUrl?: string);
20
+ put(key: string, contents: Uint8Array, options?: DiskWriteOptions): Promise<void>;
21
+ getBytes(key: string): Promise<Uint8Array>;
22
+ getStream(key: string): Promise<Readable>;
23
+ exists(key: string): Promise<boolean>;
24
+ delete(key: string): Promise<void>;
25
+ getUrl(key: string): Promise<string>;
26
+ getSignedUrl(key: string, options?: SignedUrlOptions): Promise<string>;
27
+ getMetaData(key: string): Promise<DiskMetaData>;
28
+ private require;
29
+ }
30
+ /**
31
+ * Build a {@link DiskResolver} over named in-memory disks. The first name is the default; a single
32
+ * unnamed disk is named `default`. Returns the resolver plus the disk map for assertions.
33
+ *
34
+ * ```ts
35
+ * const { resolve, disks } = inMemoryDiskResolver(['fs'])
36
+ * const storage = new StorageManager({ default: 'fs', resolve })
37
+ * ```
38
+ */
39
+ export declare function inMemoryDiskResolver(names?: string[]): {
40
+ resolve: DiskResolver;
41
+ disks: Record<string, InMemoryDisk>;
42
+ };
43
+ export {};
44
+ //# sourceMappingURL=in_memory_disk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in_memory_disk.d.ts","sourceRoot":"","sources":["../../../src/testing/in_memory_disk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EACV,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAErB,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,IAAI,CAAC;CACpB;AAED;;;;GAIG;AACH,qBAAa,YAAa,YAAW,IAAI;IAIrC,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,QAAQ,CAAC,KAAK,0BAAiC;;IAG7C,8CAA8C;IAC7B,OAAO,SAAkB;IAGtC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjF,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAK1C,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIpC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAKtE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IASrD,OAAO,CAAC,OAAO;CAKhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,GAAE,MAAM,EAAgB,GAAG;IACnE,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACrC,CAWA"}
@@ -0,0 +1,80 @@
1
+ import { Readable } from 'node:stream';
2
+ /**
3
+ * In-memory {@link Disk} satisfying the same structural contract as an `@adonisjs/drive` disk.
4
+ * Use it (via {@link inMemoryDiskResolver}) to drive the media-library and attachment layers in
5
+ * tests without a real filesystem or S3.
6
+ */
7
+ export class InMemoryDisk {
8
+ baseUrl;
9
+ files = new Map();
10
+ constructor(
11
+ /** Used to render `getUrl`/`getSignedUrl`. */
12
+ baseUrl = 'memory://disk') {
13
+ this.baseUrl = baseUrl;
14
+ }
15
+ async put(key, contents, options) {
16
+ this.files.set(key, {
17
+ data: Buffer.from(contents),
18
+ contentType: options?.contentType,
19
+ lastModified: new Date(),
20
+ });
21
+ }
22
+ async getBytes(key) {
23
+ const file = this.require(key);
24
+ return Buffer.from(file.data);
25
+ }
26
+ async getStream(key) {
27
+ return Readable.from(this.require(key).data);
28
+ }
29
+ async exists(key) {
30
+ return this.files.has(key);
31
+ }
32
+ async delete(key) {
33
+ this.files.delete(key);
34
+ }
35
+ async getUrl(key) {
36
+ return `${this.baseUrl}/${key}`;
37
+ }
38
+ async getSignedUrl(key, options) {
39
+ const expires = options?.expiresIn ?? '';
40
+ return `${this.baseUrl}/${key}?signature=fake&expires=${expires}`;
41
+ }
42
+ async getMetaData(key) {
43
+ const file = this.require(key);
44
+ return {
45
+ contentLength: file.data.byteLength,
46
+ contentType: file.contentType,
47
+ lastModified: file.lastModified,
48
+ };
49
+ }
50
+ require(key) {
51
+ const file = this.files.get(key);
52
+ if (!file)
53
+ throw new Error(`InMemoryDisk: file not found: ${key}`);
54
+ return file;
55
+ }
56
+ }
57
+ /**
58
+ * Build a {@link DiskResolver} over named in-memory disks. The first name is the default; a single
59
+ * unnamed disk is named `default`. Returns the resolver plus the disk map for assertions.
60
+ *
61
+ * ```ts
62
+ * const { resolve, disks } = inMemoryDiskResolver(['fs'])
63
+ * const storage = new StorageManager({ default: 'fs', resolve })
64
+ * ```
65
+ */
66
+ export function inMemoryDiskResolver(names = ['default']) {
67
+ const disks = {};
68
+ for (const name of names)
69
+ disks[name] = new InMemoryDisk(`memory://${name}`);
70
+ const fallback = names[0] ?? 'default';
71
+ const resolve = (name) => {
72
+ const key = name ?? fallback;
73
+ const disk = disks[key];
74
+ if (!disk)
75
+ throw new Error(`InMemoryDisk resolver: unknown disk "${key}"`);
76
+ return disk;
77
+ };
78
+ return { resolve, disks };
79
+ }
80
+ //# sourceMappingURL=in_memory_disk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in_memory_disk.js","sourceRoot":"","sources":["../../../src/testing/in_memory_disk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { MediaRecord } from '../media_record.js';
2
+ import type { MediaStore } from '../media_store.js';
3
+ /** In-memory {@link MediaStore} for tests and scratch apps. */
4
+ export declare class InMemoryMediaStore implements MediaStore {
5
+ private readonly records;
6
+ save(record: MediaRecord): Promise<MediaRecord>;
7
+ find(id: string): Promise<MediaRecord | null>;
8
+ listByOwner(ownerType: string, ownerId: string, collection?: string): Promise<MediaRecord[]>;
9
+ delete(id: string): Promise<void>;
10
+ nextOrder(ownerType: string, ownerId: string, collection: string): Promise<number>;
11
+ }
12
+ //# sourceMappingURL=in_memory_media_store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in_memory_media_store.d.ts","sourceRoot":"","sources":["../../../src/testing/in_memory_media_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,+DAA+D;AAC/D,qBAAa,kBAAmB,YAAW,UAAU;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAEpD,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAM/C,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAK7C,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,EAAE,CAAC;IAYnB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAIzF"}
@@ -0,0 +1,29 @@
1
+ /** In-memory {@link MediaStore} for tests and scratch apps. */
2
+ export class InMemoryMediaStore {
3
+ records = new Map();
4
+ async save(record) {
5
+ const copy = { ...record };
6
+ this.records.set(copy.id, copy);
7
+ return { ...copy };
8
+ }
9
+ async find(id) {
10
+ const found = this.records.get(id);
11
+ return found ? { ...found } : null;
12
+ }
13
+ async listByOwner(ownerType, ownerId, collection) {
14
+ return [...this.records.values()]
15
+ .filter((r) => r.ownerType === ownerType &&
16
+ r.ownerId === ownerId &&
17
+ (collection === undefined || r.collection === collection))
18
+ .sort((a, b) => a.order - b.order)
19
+ .map((r) => ({ ...r }));
20
+ }
21
+ async delete(id) {
22
+ this.records.delete(id);
23
+ }
24
+ async nextOrder(ownerType, ownerId, collection) {
25
+ const inCollection = await this.listByOwner(ownerType, ownerId, collection);
26
+ return inCollection.reduce((max, r) => Math.max(max, r.order + 1), 0);
27
+ }
28
+ }
29
+ //# sourceMappingURL=in_memory_media_store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in_memory_media_store.js","sourceRoot":"","sources":["../../../src/testing/in_memory_media_store.ts"],"names":[],"mappings":"AAGA,+DAA+D;AAC/D,MAAM,OAAO,kBAAkB;IACZ,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE1D,KAAK,CAAC,IAAI,CAAC,MAAmB;QAC5B,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAChC,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAU;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,WAAW,CACf,SAAiB,EACjB,OAAe,EACf,UAAmB;QAEnB,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;aAC9B,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,SAAS,KAAK,SAAS;YACzB,CAAC,CAAC,OAAO,KAAK,OAAO;YACrB,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAC5D;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;aACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,SAAiB,EAAE,OAAe,EAAE,UAAkB;QACpE,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAC5E,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxE,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export { InMemoryMediaStore } from './in_memory_media_store.js';
2
+ export { InMemoryDisk, inMemoryDiskResolver } from './in_memory_disk.js';
3
+ export { FakeImageProcessor } from './fake_image_processor.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { InMemoryMediaStore } from './in_memory_media_store.js';
2
+ export { InMemoryDisk, inMemoryDiskResolver } from './in_memory_disk.js';
3
+ export { FakeImageProcessor } from './fake_image_processor.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}