@jrmc/adonis-attachment 3.3.0-beta.4 → 4.0.0-beta.1
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/build/index.d.ts +2 -1
- package/build/index.js +2 -1
- package/build/services/regenerate_service.d.ts +8 -0
- package/build/services/regenerate_service.js +29 -0
- package/build/src/attachment_manager.d.ts +3 -4
- package/build/src/attachment_manager.js +2 -5
- package/build/src/attachments/attachment_base.d.ts +1 -0
- package/build/src/attachments/attachment_base.js +3 -0
- package/build/src/attachments/variant_attachment.d.ts +2 -0
- package/build/src/attachments/variant_attachment.js +4 -0
- package/build/src/converter_manager.d.ts +2 -2
- package/build/src/converter_manager.js +88 -48
- package/build/src/converters/autodetect_converter.d.ts +2 -1
- package/build/src/converters/autodetect_converter.js +7 -9
- package/build/src/converters/video_thumbnail_converter.d.ts +2 -2
- package/build/src/converters/video_thumbnail_converter.js +1 -1
- package/build/src/decorators/attachment.d.ts +2 -2
- package/build/src/define_config.js +8 -4
- package/build/src/services/record_with_attachment.d.ts +8 -5
- package/build/src/services/record_with_attachment.js +89 -43
- package/build/src/types/attachment.d.ts +3 -0
- package/build/src/types/config.d.ts +1 -1
- package/build/src/types/converter.d.ts +5 -2
- package/build/src/types/index.d.ts +1 -0
- package/build/src/types/index.js +1 -0
- package/build/src/types/mixin.d.ts +3 -3
- package/build/src/types/regenerate.d.ts +5 -0
- package/build/src/types/regenerate.js +1 -0
- package/build/src/types/service.d.ts +4 -3
- package/build/src/utils/hooks.js +5 -5
- package/package.json +1 -1
- package/build/src/mixins/attachmentable.d.ts +0 -151
- package/build/src/mixins/attachmentable.js +0 -31
package/build/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import attachmentManager from './services/main.js';
|
|
2
|
+
import RegenerateService from './services/regenerate_service.js';
|
|
2
3
|
export { configure } from './configure.js';
|
|
3
4
|
export { Attachment } from './src/attachments/attachment.js';
|
|
4
5
|
export { attachment } from './src/decorators/attachment.js';
|
|
5
6
|
export { attachments } from './src/decorators/attachment.js';
|
|
6
7
|
export { defineConfig } from './src/define_config.js';
|
|
7
|
-
export { Attachmentable } from './src/mixins/attachmentable.js';
|
|
8
8
|
export * as errors from './src/errors.js';
|
|
9
9
|
export { attachmentManager };
|
|
10
|
+
export { RegenerateService };
|
|
10
11
|
export { type AttachmentVariants } from './src/types/config.js';
|
package/build/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import attachmentManager from './services/main.js';
|
|
2
|
+
import RegenerateService from './services/regenerate_service.js';
|
|
2
3
|
export { configure } from './configure.js';
|
|
3
4
|
export { Attachment } from './src/attachments/attachment.js';
|
|
4
5
|
export { attachment } from './src/decorators/attachment.js';
|
|
5
6
|
export { attachments } from './src/decorators/attachment.js';
|
|
6
7
|
export { defineConfig } from './src/define_config.js';
|
|
7
|
-
export { Attachmentable } from './src/mixins/attachmentable.js';
|
|
8
8
|
export * as errors from './src/errors.js';
|
|
9
9
|
export { attachmentManager };
|
|
10
|
+
export { RegenerateService };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LucidRow, LucidModel } from '@adonisjs/lucid/types/model';
|
|
2
|
+
import type { RegenerateOptions } from '../src/types/regenerate.js';
|
|
3
|
+
export default class RegenerateService {
|
|
4
|
+
#private;
|
|
5
|
+
model(Model: LucidModel, options?: RegenerateOptions): this;
|
|
6
|
+
row(row: LucidRow, options?: RegenerateOptions): this;
|
|
7
|
+
run(): Promise<void | void[]>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import RecordWithAttachment from '../src/services/record_with_attachment.js';
|
|
2
|
+
export default class RegenerateService {
|
|
3
|
+
#Model;
|
|
4
|
+
#row;
|
|
5
|
+
#options;
|
|
6
|
+
model(Model, options = {}) {
|
|
7
|
+
this.#Model = Model;
|
|
8
|
+
this.#options = options;
|
|
9
|
+
return this;
|
|
10
|
+
}
|
|
11
|
+
row(row, options = {}) {
|
|
12
|
+
this.#row = row;
|
|
13
|
+
this.#options = options;
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
async run() {
|
|
17
|
+
if (this.#row) {
|
|
18
|
+
const record = new RecordWithAttachment(this.#row);
|
|
19
|
+
return record.regenerateVariants(this.#options);
|
|
20
|
+
}
|
|
21
|
+
else if (this.#Model) {
|
|
22
|
+
const entities = await this.#Model.all();
|
|
23
|
+
return Promise.all(entities.map(async (entity) => {
|
|
24
|
+
const record = new RecordWithAttachment(entity);
|
|
25
|
+
return record.regenerateVariants(this.#options);
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
import type { DriveService, SignedURLOptions } from '@adonisjs/drive/types';
|
|
8
8
|
import type { MultipartFile } from '@adonisjs/core/bodyparser';
|
|
9
9
|
import type { AttachmentAttributes, AttachmentBase, Attachment as AttachmentType } from './types/attachment.js';
|
|
10
|
+
import type { ResolvedAttachmentConfig } from './define_config.js';
|
|
10
11
|
import { DeferQueue } from '@poppinss/defer';
|
|
11
12
|
import Converter from './converters/converter.js';
|
|
12
|
-
import { ResolvedAttachmentConfig } from './define_config.js';
|
|
13
13
|
export declare class AttachmentManager<KnownConverters extends Record<string, Converter>> {
|
|
14
14
|
#private;
|
|
15
15
|
queue: DeferQueue;
|
|
16
16
|
constructor(config: ResolvedAttachmentConfig<KnownConverters>, drive: DriveService);
|
|
17
|
-
getConfig(): ResolvedAttachmentConfig<KnownConverters>;
|
|
18
17
|
createFromDbResponse(response?: string | JSON): AttachmentType | null;
|
|
19
18
|
createFromFile(input: MultipartFile): Promise<AttachmentType>;
|
|
20
19
|
createFromFiles(inputs: MultipartFile[]): Promise<(AttachmentBase & {
|
|
@@ -34,6 +33,6 @@ export declare class AttachmentManager<KnownConverters extends Record<string, Co
|
|
|
34
33
|
getConverter(key: string): Promise<void | Converter>;
|
|
35
34
|
computeUrl(attachment: AttachmentType | AttachmentBase, signedUrlOptions?: SignedURLOptions): Promise<void>;
|
|
36
35
|
preComputeUrl(attachment: AttachmentType): Promise<void>;
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
write(attachment: AttachmentBase): Promise<void>;
|
|
37
|
+
remove(attachment: AttachmentBase): Promise<void>;
|
|
39
38
|
}
|
|
@@ -23,9 +23,6 @@ export class AttachmentManager {
|
|
|
23
23
|
const concurrency = this.#config.queue?.concurrency || 1;
|
|
24
24
|
this.queue = new DeferQueue({ concurrency });
|
|
25
25
|
}
|
|
26
|
-
getConfig() {
|
|
27
|
-
return this.#config;
|
|
28
|
-
}
|
|
29
26
|
createFromDbResponse(response) {
|
|
30
27
|
if (response === null) {
|
|
31
28
|
return null;
|
|
@@ -125,7 +122,7 @@ export class AttachmentManager {
|
|
|
125
122
|
}
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
|
-
async
|
|
125
|
+
async write(attachment) {
|
|
129
126
|
const destinationPath = attachment.path;
|
|
130
127
|
if (attachment.options?.meta) {
|
|
131
128
|
attachment.meta = await ExifAdapter.exif(attachment.input, this.#config);
|
|
@@ -140,7 +137,7 @@ export class AttachmentManager {
|
|
|
140
137
|
await attachment.getDisk().copyFromFs(attachment.input, destinationPath);
|
|
141
138
|
}
|
|
142
139
|
}
|
|
143
|
-
async
|
|
140
|
+
async remove(attachment) {
|
|
144
141
|
if (attachment.path) {
|
|
145
142
|
await attachment.getDisk().delete(attachment.path);
|
|
146
143
|
if (attachment instanceof Attachment) {
|
|
@@ -30,6 +30,7 @@ export declare class AttachmentBase implements AttachmentBaseInterface {
|
|
|
30
30
|
* Methods
|
|
31
31
|
*/
|
|
32
32
|
getDisk(): import("flydrive").Disk;
|
|
33
|
+
getStream(): Promise<import("stream").Readable>;
|
|
33
34
|
getUrl(): Promise<string>;
|
|
34
35
|
getSignedUrl(signedUrlOptions?: SignedURLOptions): Promise<string>;
|
|
35
36
|
setOptions(options: LucidOptions): this;
|
|
@@ -8,11 +8,13 @@ import type { DriveService } from '@adonisjs/drive/types';
|
|
|
8
8
|
import type { VariantAttributes, Variant as VariantInterface } from '../types/attachment.js';
|
|
9
9
|
import type { Input } from '../types/input.js';
|
|
10
10
|
import { AttachmentBase } from './attachment_base.js';
|
|
11
|
+
import { BlurhashOptions } from '../types/converter.js';
|
|
11
12
|
export declare class Variant extends AttachmentBase implements VariantInterface {
|
|
12
13
|
#private;
|
|
13
14
|
key: string;
|
|
14
15
|
blurhash?: string;
|
|
15
16
|
constructor(drive: DriveService, attributes: VariantAttributes, input?: Input);
|
|
17
|
+
generateBlurhash(options?: BlurhashOptions): Promise<void>;
|
|
16
18
|
/**
|
|
17
19
|
* Getters
|
|
18
20
|
*/
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
7
|
import { AttachmentBase } from './attachment_base.js';
|
|
8
|
+
import { imageToBlurhash } from '../utils/helpers.js';
|
|
8
9
|
export class Variant extends AttachmentBase {
|
|
9
10
|
key;
|
|
10
11
|
#folder;
|
|
@@ -15,6 +16,9 @@ export class Variant extends AttachmentBase {
|
|
|
15
16
|
this.#folder = attributes.folder;
|
|
16
17
|
this.blurhash = attributes.blurhash;
|
|
17
18
|
}
|
|
19
|
+
async generateBlurhash(options) {
|
|
20
|
+
this.blurhash = await imageToBlurhash(this.input, options);
|
|
21
|
+
}
|
|
18
22
|
/**
|
|
19
23
|
* Getters
|
|
20
24
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ConverterInitializeAttributes } from './types/converter.js';
|
|
2
2
|
export declare class ConverterManager {
|
|
3
3
|
#private;
|
|
4
|
-
constructor({ record, attributeName, options }: ConverterInitializeAttributes);
|
|
5
|
-
|
|
4
|
+
constructor({ record, attributeName, options, filters }: ConverterInitializeAttributes);
|
|
5
|
+
run(): Promise<void>;
|
|
6
6
|
}
|
|
@@ -3,71 +3,93 @@ import string from '@adonisjs/core/helpers/string';
|
|
|
3
3
|
import db from '@adonisjs/lucid/services/db';
|
|
4
4
|
import attachmentManager from '../services/main.js';
|
|
5
5
|
import * as errors from './errors.js';
|
|
6
|
-
import {
|
|
6
|
+
import { streamToTempFile } from './utils/helpers.js';
|
|
7
7
|
export class ConverterManager {
|
|
8
8
|
#record;
|
|
9
9
|
#attributeName;
|
|
10
10
|
#options;
|
|
11
|
-
|
|
11
|
+
#filters;
|
|
12
|
+
constructor({ record, attributeName, options, filters }) {
|
|
12
13
|
this.#record = record;
|
|
13
14
|
this.#attributeName = attributeName;
|
|
14
15
|
this.#options = options;
|
|
16
|
+
this.#filters = filters;
|
|
15
17
|
}
|
|
16
|
-
async
|
|
17
|
-
|
|
18
|
+
async run() {
|
|
19
|
+
const attachments = this.#record.getAttachments({
|
|
18
20
|
attributeName: this.#attributeName,
|
|
19
21
|
});
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if ((typeof converter.options.blurhash !== 'boolean' &&
|
|
39
|
-
converter.options.blurhash.enabled === true) ||
|
|
40
|
-
converter.options.blurhash === true) {
|
|
41
|
-
try {
|
|
42
|
-
const options = typeof converter.options.blurhash !== 'boolean'
|
|
43
|
-
? converter.options.blurhash
|
|
44
|
-
: undefined;
|
|
45
|
-
variant.blurhash = await imageToBlurhash(variant.input, options);
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
logger.error(error.message);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
await attachmentManager.save(variant);
|
|
53
|
-
}
|
|
22
|
+
const variants = [];
|
|
23
|
+
await this.#purge(attachments);
|
|
24
|
+
if (!attachments || !this.#options.variants || !this.#options.variants.length) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for await (const key of this.#options.variants) {
|
|
28
|
+
if (this.#filters?.variants !== undefined && !this.#filters?.variants?.includes(key)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const converter = (await attachmentManager.getConverter(key));
|
|
32
|
+
if (converter) {
|
|
33
|
+
for await (const attachment of attachments) {
|
|
34
|
+
const variant = await this.#generate({
|
|
35
|
+
key,
|
|
36
|
+
attachment,
|
|
37
|
+
converter
|
|
38
|
+
});
|
|
39
|
+
variants.push(variant);
|
|
54
40
|
}
|
|
55
41
|
}
|
|
56
42
|
}
|
|
57
|
-
|
|
58
|
-
|
|
43
|
+
return this.#commit(attachments, () => {
|
|
44
|
+
for (let i = 0; i < variants.length; i++) {
|
|
45
|
+
attachmentManager.remove(variants[i]);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async #generate({ key, attachment, converter }) {
|
|
50
|
+
let input = attachment.input;
|
|
51
|
+
if (!input) {
|
|
52
|
+
input = await streamToTempFile(await attachment.getStream());
|
|
59
53
|
}
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
const output = await converter.handle({
|
|
55
|
+
input,
|
|
56
|
+
options: converter.options,
|
|
57
|
+
});
|
|
58
|
+
if (output === undefined) {
|
|
59
|
+
throw new errors.E_CANNOT_PATH_BY_CONVERTER();
|
|
62
60
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
const variant = await attachment.createVariant(key, output);
|
|
62
|
+
if (converter.options.blurhash) {
|
|
63
|
+
if ((typeof converter.options.blurhash !== 'boolean' &&
|
|
64
|
+
converter.options.blurhash.enabled === true) ||
|
|
65
|
+
converter.options.blurhash === true) {
|
|
66
|
+
try {
|
|
67
|
+
const options = typeof converter.options.blurhash !== 'boolean'
|
|
68
|
+
? converter.options.blurhash
|
|
69
|
+
: undefined;
|
|
70
|
+
await variant.generateBlurhash(options);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.error(error.message);
|
|
68
74
|
}
|
|
69
75
|
}
|
|
70
|
-
}
|
|
76
|
+
}
|
|
77
|
+
await attachmentManager.write(variant);
|
|
78
|
+
return variant;
|
|
79
|
+
}
|
|
80
|
+
async #commit(attachments, rollback) {
|
|
81
|
+
const Model = this.#record.row.constructor;
|
|
82
|
+
const id = this.#record.row.$attributes['id'];
|
|
83
|
+
const data = {};
|
|
84
|
+
const index = string.snakeCase(this.#attributeName);
|
|
85
|
+
if (Array.isArray(this.#record.row.$original[this.#attributeName])) {
|
|
86
|
+
data[index] = JSON.stringify(attachments.map((att) => att.toObject()));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
data[index] = JSON.stringify(attachments[0].toObject());
|
|
90
|
+
}
|
|
91
|
+
const trx = await db.transaction();
|
|
92
|
+
trx.after('rollback', rollback);
|
|
71
93
|
try {
|
|
72
94
|
await trx.query().from(Model.table).where('id', id).update(data);
|
|
73
95
|
return trx.commit();
|
|
@@ -76,4 +98,22 @@ export class ConverterManager {
|
|
|
76
98
|
return trx.rollback();
|
|
77
99
|
}
|
|
78
100
|
}
|
|
101
|
+
async #purge(attachments) {
|
|
102
|
+
return Promise.all(attachments.map(async (attachment) => {
|
|
103
|
+
if (attachment.variants) {
|
|
104
|
+
await Promise.all(attachment.variants.map(async (variant) => {
|
|
105
|
+
if (this.#filters?.variants !== undefined && !this.#filters?.variants?.includes(variant.key)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
return attachmentManager.remove(variant);
|
|
109
|
+
}));
|
|
110
|
+
if (this.#filters?.variants !== undefined) {
|
|
111
|
+
attachment.variants = await attachment.variants.filter((variant) => !this.#filters?.variants?.includes(variant.key));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
attachment.variants = [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
79
119
|
}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
7
|
import type { ConverterAttributes } from '../types/converter.js';
|
|
8
|
+
import type { Input } from '../types/input.js';
|
|
8
9
|
import Converter from './converter.js';
|
|
9
10
|
export default class AutodetectConverter extends Converter {
|
|
10
|
-
handle({ input, options }: ConverterAttributes): Promise<
|
|
11
|
+
handle({ input, options }: ConverterAttributes): Promise<Input | undefined>;
|
|
11
12
|
}
|
|
@@ -18,17 +18,15 @@ export default class AutodetectConverter extends Converter {
|
|
|
18
18
|
else {
|
|
19
19
|
fileType = await fileTypeFromFile(input);
|
|
20
20
|
}
|
|
21
|
-
if (fileType?.mime.includes('
|
|
22
|
-
converter = new ImageConverter(options, this.binPaths);
|
|
23
|
-
}
|
|
24
|
-
else if (fileType?.mime.includes('video')) {
|
|
21
|
+
if (fileType?.mime.includes('video')) {
|
|
25
22
|
converter = new VideoThumnailConverter(options, this.binPaths);
|
|
26
23
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
input,
|
|
30
|
-
options,
|
|
31
|
-
});
|
|
24
|
+
else {
|
|
25
|
+
converter = new ImageConverter(options, this.binPaths);
|
|
32
26
|
}
|
|
27
|
+
return converter.handle({
|
|
28
|
+
input,
|
|
29
|
+
options,
|
|
30
|
+
});
|
|
33
31
|
}
|
|
34
32
|
}
|
|
@@ -8,6 +8,6 @@ import type { ConverterAttributes } from '../types/converter.js';
|
|
|
8
8
|
import type { Input } from '../types/input.js';
|
|
9
9
|
import Converter from './converter.js';
|
|
10
10
|
export default class VideoThumbnailConvert extends Converter {
|
|
11
|
-
handle({ input, options }: ConverterAttributes): Promise<
|
|
12
|
-
videoToImage(ffmpeg: Function, input: Input): Promise<string |
|
|
11
|
+
handle({ input, options }: ConverterAttributes): Promise<Input | undefined>;
|
|
12
|
+
videoToImage(ffmpeg: Function, input: Input): Promise<string | undefined>;
|
|
13
13
|
}
|
|
@@ -16,7 +16,7 @@ export default class VideoThumbnailConvert extends Converter {
|
|
|
16
16
|
const filePath = await this.videoToImage(ffmpeg, input);
|
|
17
17
|
if (options && filePath) {
|
|
18
18
|
const converter = new ImageConverter();
|
|
19
|
-
return
|
|
19
|
+
return converter.handle({
|
|
20
20
|
input: filePath,
|
|
21
21
|
options,
|
|
22
22
|
});
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { LucidModel } from '@adonisjs/lucid/types/model';
|
|
8
8
|
import type { LucidOptions } from '../types/attachment.js';
|
|
9
|
-
import type {
|
|
9
|
+
import type { AttributeOfRowWithAttachment } from '../types/mixin.js';
|
|
10
10
|
export declare const bootModel: (model: LucidModel & {
|
|
11
|
-
$attachments:
|
|
11
|
+
$attachments: AttributeOfRowWithAttachment;
|
|
12
12
|
}) => void;
|
|
13
13
|
export declare const attachment: (options?: LucidOptions) => (target: any, attributeName: string) => void;
|
|
14
14
|
export declare const attachments: (options?: LucidOptions) => (target: any, attributeName: string) => void;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
7
|
import { configProvider } from '@adonisjs/core';
|
|
8
|
-
// export function defineConfig<T extends AttachmentConfig>(config: T): T {
|
|
9
8
|
export function defineConfig(config) {
|
|
10
9
|
return configProvider.create(async (_app) => {
|
|
11
10
|
const convertersList = Object.keys(config.converters || {});
|
|
@@ -14,9 +13,14 @@ export function defineConfig(config) {
|
|
|
14
13
|
for (let converterName of convertersList) {
|
|
15
14
|
const converter = config.converters[converterName];
|
|
16
15
|
const binConfig = config.bin;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
try {
|
|
17
|
+
const { default: value } = await converter.converter();
|
|
18
|
+
const Converter = value;
|
|
19
|
+
converters[converterName] = new Converter(converter.options, binConfig);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error(`Failed to load converter ${converterName}:`, error);
|
|
23
|
+
}
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
return {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RowWithAttachment } from '../types/mixin.js';
|
|
2
2
|
import type { Attachment as AttachmentType } from '../types/attachment.js';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type { RecordWithAttachment as RecordWithAttachmentImplementation } from '../types/service.js';
|
|
4
|
+
import type { RegenerateOptions } from '../types/regenerate.js';
|
|
5
|
+
export default class RecordWithAttachment implements RecordWithAttachmentImplementation {
|
|
5
6
|
#private;
|
|
6
|
-
constructor(
|
|
7
|
+
constructor(row: RowWithAttachment);
|
|
7
8
|
/**
|
|
8
9
|
* During commit, we should cleanup the old detached files
|
|
9
10
|
*/
|
|
@@ -18,11 +19,13 @@ export default class Record implements RecordImplementation {
|
|
|
18
19
|
}): Promise<void>;
|
|
19
20
|
preComputeUrl(): Promise<void>;
|
|
20
21
|
generateVariants(): Promise<void>;
|
|
22
|
+
regenerateVariants(options?: RegenerateOptions): Promise<void>;
|
|
21
23
|
detach(): Promise<PromiseSettledResult<void>[]>;
|
|
22
24
|
detachAll(): Promise<PromiseSettledResult<void>[]>;
|
|
23
|
-
get
|
|
25
|
+
get row(): RowWithAttachment;
|
|
24
26
|
getAttachments(options: {
|
|
25
27
|
attributeName: string;
|
|
26
28
|
requiredOriginal?: boolean;
|
|
29
|
+
requiredDirty?: boolean;
|
|
27
30
|
}): AttachmentType[];
|
|
28
31
|
}
|
|
@@ -4,33 +4,33 @@ import { Attachment } from '../attachments/attachment.js';
|
|
|
4
4
|
import { optionsSym } from '../utils/symbols.js';
|
|
5
5
|
import { ConverterManager } from '../converter_manager.js';
|
|
6
6
|
import { E_CANNOT_CREATE_VARIANT } from '../errors.js';
|
|
7
|
-
export default class
|
|
8
|
-
#
|
|
9
|
-
constructor(
|
|
10
|
-
this.#
|
|
11
|
-
if (!this.#
|
|
7
|
+
export default class RecordWithAttachment {
|
|
8
|
+
#row;
|
|
9
|
+
constructor(row) {
|
|
10
|
+
this.#row = row;
|
|
11
|
+
if (!this.#row.$attachments) {
|
|
12
12
|
/**
|
|
13
13
|
* Empty previous $attachments
|
|
14
14
|
*/
|
|
15
|
-
this.#
|
|
15
|
+
this.#row.$attachments = structuredClone(defaultStateAttributeMixin);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* During commit, we should cleanup the old detached files
|
|
20
20
|
*/
|
|
21
21
|
async commit() {
|
|
22
|
-
await Promise.allSettled(this.#
|
|
22
|
+
await Promise.allSettled(this.#row.$attachments.detached.map((attachment) => attachmentManager.remove(attachment)));
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* During rollback we should remove the attached files.
|
|
26
26
|
*/
|
|
27
27
|
async rollback() {
|
|
28
|
-
await Promise.allSettled(this.#
|
|
28
|
+
await Promise.allSettled(this.#row.$attachments.attached.map((attachment) => attachmentManager.remove(attachment)));
|
|
29
29
|
}
|
|
30
30
|
async persist() {
|
|
31
31
|
const attachmentAttributeNames = this.#getDirtyAttributeNamesOfAttachment();
|
|
32
32
|
/**
|
|
33
|
-
* Persist attachments before saving the
|
|
33
|
+
* Persist attachments before saving the row to the database. This
|
|
34
34
|
* way if file saving fails we will not write anything to the
|
|
35
35
|
* database
|
|
36
36
|
*/
|
|
@@ -47,7 +47,7 @@ export default class Record {
|
|
|
47
47
|
/**
|
|
48
48
|
* memorise attribute name for generate variants
|
|
49
49
|
*/
|
|
50
|
-
this.#
|
|
50
|
+
this.#row.$attachments.dirtied.push(name);
|
|
51
51
|
for (let i = 0; i < newAttachments.length; i++) {
|
|
52
52
|
if (originalAttachments.includes(newAttachments[i])) {
|
|
53
53
|
continue;
|
|
@@ -57,22 +57,22 @@ export default class Record {
|
|
|
57
57
|
* file.
|
|
58
58
|
*/
|
|
59
59
|
if (newAttachments[i]) {
|
|
60
|
-
newAttachments[i].setOptions(options).makeFolder(this.#
|
|
61
|
-
this.#
|
|
60
|
+
newAttachments[i].setOptions(options).makeFolder(this.#row);
|
|
61
|
+
this.#row.$attachments.attached.push(newAttachments[i]);
|
|
62
62
|
/**
|
|
63
63
|
* Also write the file to the disk right away
|
|
64
64
|
*/
|
|
65
|
-
await attachmentManager.
|
|
65
|
+
await attachmentManager.write(newAttachments[i]);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
}));
|
|
69
69
|
}
|
|
70
70
|
async transaction(options = { enabledRollback: true }) {
|
|
71
71
|
try {
|
|
72
|
-
if (this.#
|
|
73
|
-
this.#
|
|
72
|
+
if (this.#row.$trx) {
|
|
73
|
+
this.#row.$trx.after('commit', () => this.commit());
|
|
74
74
|
if (options.enabledRollback) {
|
|
75
|
-
this.#
|
|
75
|
+
this.#row.$trx.after('rollback', () => this.rollback());
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
else {
|
|
@@ -90,7 +90,7 @@ export default class Record {
|
|
|
90
90
|
const attachmentAttributeNames = this.#getAttributeNamesOfAttachment();
|
|
91
91
|
await Promise.all(attachmentAttributeNames.map(async (name) => {
|
|
92
92
|
const options = this.#getOptionsByAttributeName(name);
|
|
93
|
-
if (this.#
|
|
93
|
+
if (this.#row.$attributes[name]) {
|
|
94
94
|
const attachments = this.#getAttachmentsByAttributeName(name);
|
|
95
95
|
for (let i = 0; i < attachments.length; i++) {
|
|
96
96
|
attachments[i].setOptions(options);
|
|
@@ -100,8 +100,8 @@ export default class Record {
|
|
|
100
100
|
}));
|
|
101
101
|
}
|
|
102
102
|
async generateVariants() {
|
|
103
|
-
/* this.#
|
|
104
|
-
const attachmentAttributeNames = this.#
|
|
103
|
+
/* this.#row.$dirty is not avalable in afterSave hooks */
|
|
104
|
+
const attachmentAttributeNames = this.#row.$attachments.dirtied;
|
|
105
105
|
/**
|
|
106
106
|
* For all properties Attachment
|
|
107
107
|
* Launch async generation variants
|
|
@@ -109,7 +109,7 @@ export default class Record {
|
|
|
109
109
|
for await (const name of attachmentAttributeNames) {
|
|
110
110
|
const record = this;
|
|
111
111
|
attachmentManager.queue.push({
|
|
112
|
-
name: `${this.#
|
|
112
|
+
name: `${this.#row.constructor.name}-${name}`,
|
|
113
113
|
async run() {
|
|
114
114
|
try {
|
|
115
115
|
const converterManager = new ConverterManager({
|
|
@@ -117,7 +117,38 @@ export default class Record {
|
|
|
117
117
|
attributeName: name,
|
|
118
118
|
options: record.#getOptionsByAttributeName(name),
|
|
119
119
|
});
|
|
120
|
-
await converterManager.
|
|
120
|
+
await converterManager.run();
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
throw new E_CANNOT_CREATE_VARIANT([err.message]);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async regenerateVariants(options = {}) {
|
|
130
|
+
let attachmentAttributeNames;
|
|
131
|
+
if (options.attributes?.length) {
|
|
132
|
+
attachmentAttributeNames = options.attributes;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
attachmentAttributeNames = this.#getAttributeNamesOfAttachment();
|
|
136
|
+
}
|
|
137
|
+
for await (const name of attachmentAttributeNames) {
|
|
138
|
+
const record = this;
|
|
139
|
+
attachmentManager.queue.push({
|
|
140
|
+
name: `${this.#row.constructor.name}-${name}`,
|
|
141
|
+
async run() {
|
|
142
|
+
try {
|
|
143
|
+
const converterManager = new ConverterManager({
|
|
144
|
+
record,
|
|
145
|
+
attributeName: name,
|
|
146
|
+
options: record.#getOptionsByAttributeName(name),
|
|
147
|
+
filters: {
|
|
148
|
+
variants: options.variants
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
await converterManager.run();
|
|
121
152
|
}
|
|
122
153
|
catch (err) {
|
|
123
154
|
throw new E_CANNOT_CREATE_VARIANT([err.message]);
|
|
@@ -134,7 +165,7 @@ export default class Record {
|
|
|
134
165
|
return Promise.allSettled(attachmentAttributeNames.map((name) => {
|
|
135
166
|
let attachments = [];
|
|
136
167
|
const options = this.#getOptionsByAttributeName(name);
|
|
137
|
-
if (this.#
|
|
168
|
+
if (this.#row.$dirty[name] === null) {
|
|
138
169
|
attachments = this.#getOriginalAttachmentsByAttributeName(name);
|
|
139
170
|
}
|
|
140
171
|
else {
|
|
@@ -158,7 +189,7 @@ export default class Record {
|
|
|
158
189
|
}
|
|
159
190
|
for (let i = 0; i < attachments.length; i++) {
|
|
160
191
|
attachments[i].setOptions(options);
|
|
161
|
-
this.#
|
|
192
|
+
this.#row.$attachments.detached.push(attachments[i]);
|
|
162
193
|
}
|
|
163
194
|
}));
|
|
164
195
|
}
|
|
@@ -172,53 +203,68 @@ export default class Record {
|
|
|
172
203
|
const attachments = this.#getAttachmentsByAttributeName(name);
|
|
173
204
|
for (let i = 0; i < attachments.length; i++) {
|
|
174
205
|
attachments[i].setOptions(options);
|
|
175
|
-
this.#
|
|
206
|
+
this.#row.$attachments.detached.push(attachments[i]);
|
|
176
207
|
}
|
|
177
208
|
}));
|
|
178
209
|
}
|
|
179
|
-
get
|
|
180
|
-
return this.#
|
|
210
|
+
get row() {
|
|
211
|
+
return this.#row;
|
|
181
212
|
}
|
|
182
213
|
getAttachments(options) {
|
|
214
|
+
let attachments;
|
|
183
215
|
if (options.requiredOriginal) {
|
|
184
|
-
|
|
216
|
+
attachments = this.#getOriginalAttachmentsByAttributeName(options.attributeName);
|
|
217
|
+
}
|
|
218
|
+
else if (options.requiredDirty) {
|
|
219
|
+
attachments = this.#getDirtyAttachmentsByAttributeName(options.attributeName);
|
|
185
220
|
}
|
|
186
221
|
else {
|
|
187
|
-
|
|
222
|
+
attachments = this.#getAttachmentsByAttributeName(options.attributeName);
|
|
188
223
|
}
|
|
224
|
+
const opts = this.#getOptionsByAttributeName(options.attributeName);
|
|
225
|
+
attachments.map((attachment) => attachment.setOptions(opts).makeFolder(this.#row));
|
|
226
|
+
return attachments;
|
|
189
227
|
}
|
|
190
228
|
#getAttachmentsByAttributeName(name) {
|
|
191
|
-
if (Array.isArray(this.#
|
|
192
|
-
return this.#
|
|
229
|
+
if (Array.isArray(this.#row.$attributes[name])) {
|
|
230
|
+
return this.#row.$attributes[name];
|
|
193
231
|
}
|
|
194
|
-
return [this.#
|
|
232
|
+
return [this.#row.$attributes[name]];
|
|
195
233
|
}
|
|
196
234
|
#getOriginalAttachmentsByAttributeName(name) {
|
|
197
|
-
if (Array.isArray(this.#
|
|
198
|
-
return this.#
|
|
235
|
+
if (Array.isArray(this.#row.$original[name])) {
|
|
236
|
+
return this.#row.$original[name];
|
|
237
|
+
}
|
|
238
|
+
return [this.#row.$original[name]];
|
|
239
|
+
}
|
|
240
|
+
#getDirtyAttachmentsByAttributeName(name) {
|
|
241
|
+
if (Array.isArray(this.#row.$dirty[name])) {
|
|
242
|
+
return this.#row.$dirty[name];
|
|
199
243
|
}
|
|
200
|
-
return [this.#
|
|
244
|
+
return [this.#row.$dirty[name]];
|
|
201
245
|
}
|
|
202
246
|
#getOptionsByAttributeName(name) {
|
|
203
|
-
return this.#
|
|
247
|
+
return this.#row.constructor.prototype[optionsSym]?.[name];
|
|
204
248
|
}
|
|
205
249
|
#getAttributeNamesOfAttachment() {
|
|
206
|
-
return Object.keys(this.#
|
|
207
|
-
const value = this.#
|
|
250
|
+
return Object.keys(this.#row.$attributes).filter((name) => {
|
|
251
|
+
const value = this.#row.$attributes[name];
|
|
208
252
|
return (value instanceof Attachment ||
|
|
209
253
|
(Array.isArray(value) && value.every((item) => item instanceof Attachment)));
|
|
210
254
|
});
|
|
211
255
|
}
|
|
212
256
|
#getDirtyAttributeNamesOfAttachment() {
|
|
213
|
-
return Object.keys(this.#
|
|
214
|
-
const dirtyValue = this.#
|
|
215
|
-
const originalValue = this.#
|
|
257
|
+
return Object.keys(this.#row.$dirty).filter((name) => {
|
|
258
|
+
const dirtyValue = this.#row.$dirty[name];
|
|
259
|
+
const originalValue = this.#row.$original[name]; // if dirtyValue is null, check original type
|
|
216
260
|
const isDirtyAttachment = dirtyValue instanceof Attachment ||
|
|
217
261
|
(Array.isArray(dirtyValue) &&
|
|
218
|
-
dirtyValue.
|
|
219
|
-
dirtyValue.
|
|
262
|
+
dirtyValue.length &&
|
|
263
|
+
dirtyValue.every((item) => item instanceof Attachment));
|
|
220
264
|
const isOriginalAttachment = originalValue instanceof Attachment ||
|
|
221
|
-
(Array.isArray(originalValue) &&
|
|
265
|
+
(Array.isArray(originalValue) &&
|
|
266
|
+
originalValue.length &&
|
|
267
|
+
originalValue.every((item) => item instanceof Attachment));
|
|
222
268
|
return isDirtyAttachment || isOriginalAttachment;
|
|
223
269
|
});
|
|
224
270
|
}
|
|
@@ -10,6 +10,7 @@ import type { Exif, Input } from './input.js';
|
|
|
10
10
|
import type { Disk } from '@adonisjs/drive';
|
|
11
11
|
import type { SignedURLOptions } from '@adonisjs/drive/types';
|
|
12
12
|
import type { AttachmentVariants } from '@jrmc/adonis-attachment';
|
|
13
|
+
import { BlurhashOptions } from './converter.js';
|
|
13
14
|
export type AttachmentBase = {
|
|
14
15
|
drive: DriveService;
|
|
15
16
|
input?: Input;
|
|
@@ -25,6 +26,7 @@ export type AttachmentBase = {
|
|
|
25
26
|
options: LucidOptions;
|
|
26
27
|
makeFolder(record?: LucidRow): void;
|
|
27
28
|
getDisk(): Disk;
|
|
29
|
+
getStream(): Promise<NodeJS.ReadableStream>;
|
|
28
30
|
getUrl(): Promise<string>;
|
|
29
31
|
getSignedUrl(signedUrlOptions?: SignedURLOptions): Promise<string>;
|
|
30
32
|
setOptions(options: LucidOptions): AttachmentBase;
|
|
@@ -44,6 +46,7 @@ export type Variant = AttachmentBase & {
|
|
|
44
46
|
key: string;
|
|
45
47
|
folder: string;
|
|
46
48
|
blurhash?: string;
|
|
49
|
+
generateBlurhash(options?: BlurhashOptions): void;
|
|
47
50
|
toObject(): VariantAttributes;
|
|
48
51
|
};
|
|
49
52
|
export type LucidOptions = {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
-
import { ConfigProvider } from '@adonisjs/core/types';
|
|
8
7
|
import type { Converter, ConverterOptions } from './converter.js';
|
|
8
|
+
import { ConfigProvider } from '@adonisjs/core/types';
|
|
9
9
|
import { AttachmentManager } from '../attachment_manager.js';
|
|
10
10
|
type ImportConverter = {
|
|
11
11
|
default: unknown;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { RecordWithAttachment } from './service.js';
|
|
8
8
|
import type { BinPaths } from './config.js';
|
|
9
9
|
import type { Input } from './input.js';
|
|
10
10
|
import type { LucidOptions } from './attachment.js';
|
|
@@ -14,9 +14,12 @@ export type Converter = {
|
|
|
14
14
|
handle?: (attributes: ConverterAttributes) => Promise<Input | undefined>;
|
|
15
15
|
};
|
|
16
16
|
export type ConverterInitializeAttributes = {
|
|
17
|
-
record:
|
|
17
|
+
record: RecordWithAttachment;
|
|
18
18
|
attributeName: string;
|
|
19
19
|
options: LucidOptions;
|
|
20
|
+
filters?: {
|
|
21
|
+
variants?: string[];
|
|
22
|
+
};
|
|
20
23
|
};
|
|
21
24
|
export type ConverterAttributes = {
|
|
22
25
|
input: Input;
|
package/build/src/types/index.js
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { LucidRow } from '@adonisjs/lucid/types/model';
|
|
8
8
|
import type { Attachment } from './attachment.js';
|
|
9
|
-
export type
|
|
9
|
+
export type AttributeOfRowWithAttachment = {
|
|
10
10
|
attached: Attachment[];
|
|
11
11
|
detached: Attachment[];
|
|
12
12
|
dirtied: string[];
|
|
13
13
|
};
|
|
14
|
-
export type
|
|
15
|
-
$attachments:
|
|
14
|
+
export type RowWithAttachment = LucidRow & {
|
|
15
|
+
$attachments: AttributeOfRowWithAttachment;
|
|
16
16
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Attachment } from './attachment.js';
|
|
2
|
-
import type {
|
|
3
|
-
export interface
|
|
4
|
-
|
|
2
|
+
import type { RowWithAttachment } from './mixin.js';
|
|
3
|
+
export interface RecordWithAttachment {
|
|
4
|
+
row: RowWithAttachment;
|
|
5
5
|
commit(): Promise<void>;
|
|
6
6
|
rollback(): Promise<void>;
|
|
7
7
|
persist(): Promise<void>;
|
|
@@ -13,5 +13,6 @@ export interface Record {
|
|
|
13
13
|
getAttachments(options: {
|
|
14
14
|
attributeName: string;
|
|
15
15
|
requiredOriginal?: boolean;
|
|
16
|
+
requiredDirty?: boolean;
|
|
16
17
|
}): Attachment[];
|
|
17
18
|
}
|
package/build/src/utils/hooks.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
7
|
+
import RecordWithAttachment from '../services/record_with_attachment.js';
|
|
8
8
|
// @afterFind()
|
|
9
9
|
export const afterFindHook = async (instance) => {
|
|
10
10
|
const modelInstance = instance;
|
|
11
|
-
const model = new
|
|
11
|
+
const model = new RecordWithAttachment(modelInstance);
|
|
12
12
|
await model.preComputeUrl();
|
|
13
13
|
};
|
|
14
14
|
// @afterFetch()
|
|
@@ -20,7 +20,7 @@ export const afterFetchHook = async (instance) => {
|
|
|
20
20
|
// @beforeSave()
|
|
21
21
|
export const beforeSaveHook = async (instance) => {
|
|
22
22
|
const modelInstance = instance;
|
|
23
|
-
const model = new
|
|
23
|
+
const model = new RecordWithAttachment(modelInstance);
|
|
24
24
|
await model.detach();
|
|
25
25
|
await model.persist();
|
|
26
26
|
await model.transaction();
|
|
@@ -28,13 +28,13 @@ export const beforeSaveHook = async (instance) => {
|
|
|
28
28
|
// @afterSave()
|
|
29
29
|
export const afterSaveHook = async (instance) => {
|
|
30
30
|
const modelInstance = instance;
|
|
31
|
-
const model = new
|
|
31
|
+
const model = new RecordWithAttachment(modelInstance);
|
|
32
32
|
await model.generateVariants();
|
|
33
33
|
};
|
|
34
34
|
// @beforeDelete()
|
|
35
35
|
export const beforeDeleteHook = async (instance) => {
|
|
36
36
|
const modelInstance = instance;
|
|
37
|
-
const model = new
|
|
37
|
+
const model = new RecordWithAttachment(modelInstance);
|
|
38
38
|
await model.detachAll();
|
|
39
39
|
await model.transaction({ enabledRollback: false });
|
|
40
40
|
};
|
package/package.json
CHANGED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @jrmc/adonis-attachment
|
|
3
|
-
*
|
|
4
|
-
* @license MIT
|
|
5
|
-
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
-
*/
|
|
7
|
-
import type { BaseModel } from '@adonisjs/lucid/orm';
|
|
8
|
-
import type { NormalizeConstructor } from '@adonisjs/core/types/helpers';
|
|
9
|
-
import type { AttributeOfModelWithAttachment } from '../types/mixin.js';
|
|
10
|
-
type Constructor = NormalizeConstructor<typeof BaseModel>;
|
|
11
|
-
export declare function Attachmentable<T extends Constructor>(superclass: T): {
|
|
12
|
-
new (...args: any[]): {
|
|
13
|
-
$attachments: AttributeOfModelWithAttachment;
|
|
14
|
-
$attributes: import("@adonisjs/lucid/types/model").ModelObject;
|
|
15
|
-
$extras: import("@adonisjs/lucid/types/model").ModelObject;
|
|
16
|
-
$original: import("@adonisjs/lucid/types/model").ModelObject;
|
|
17
|
-
$preloaded: {
|
|
18
|
-
[relation: string]: import("@adonisjs/lucid/types/model").LucidRow | import("@adonisjs/lucid/types/model").LucidRow[];
|
|
19
|
-
};
|
|
20
|
-
$columns: undefined;
|
|
21
|
-
$sideloaded: import("@adonisjs/lucid/types/model").ModelObject;
|
|
22
|
-
$primaryKeyValue?: number | string;
|
|
23
|
-
$isPersisted: boolean;
|
|
24
|
-
$isNew: boolean;
|
|
25
|
-
$isLocal: boolean;
|
|
26
|
-
$dirty: import("@adonisjs/lucid/types/model").ModelObject;
|
|
27
|
-
$isDirty: boolean;
|
|
28
|
-
$isDeleted: boolean;
|
|
29
|
-
$options?: import("@adonisjs/lucid/types/model").ModelOptions;
|
|
30
|
-
$trx?: import("@adonisjs/lucid/types/database").TransactionClientContract;
|
|
31
|
-
$setOptionsAndTrx(options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): void;
|
|
32
|
-
useTransaction(trx: import("@adonisjs/lucid/types/database").TransactionClientContract): /*elided*/ any;
|
|
33
|
-
useConnection(connection: string): /*elided*/ any;
|
|
34
|
-
$getQueryFor(action: "insert", client: import("@adonisjs/lucid/types/database").QueryClientContract): ReturnType<import("@adonisjs/lucid/types/database").QueryClientContract["insertQuery"]>;
|
|
35
|
-
$getQueryFor(action: "update" | "delete" | "refresh", client: import("@adonisjs/lucid/types/database").QueryClientContract): import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<import("@adonisjs/lucid/types/model").LucidModel>;
|
|
36
|
-
$setAttribute(key: string, value: any): void;
|
|
37
|
-
$getAttribute(key: string): any;
|
|
38
|
-
$getAttributeFromCache(key: string, callback: import("@adonisjs/lucid/types/model").CacheNode["getter"]): any;
|
|
39
|
-
$hasRelated(key: string): boolean;
|
|
40
|
-
$setRelated(key: string, result: import("@adonisjs/lucid/types/querybuilder").OneOrMany<import("@adonisjs/lucid/types/model").LucidRow> | null): void;
|
|
41
|
-
$pushRelated(key: string, result: import("@adonisjs/lucid/types/querybuilder").OneOrMany<import("@adonisjs/lucid/types/model").LucidRow> | null): void;
|
|
42
|
-
$getRelated(key: string, defaultValue?: any): import("@adonisjs/lucid/types/querybuilder").OneOrMany<import("@adonisjs/lucid/types/model").LucidRow> | undefined | null;
|
|
43
|
-
$consumeAdapterResult(adapterResult: import("@adonisjs/lucid/types/model").ModelObject, sideloadAttributes?: import("@adonisjs/lucid/types/model").ModelObject): void;
|
|
44
|
-
$hydrateOriginals(): void;
|
|
45
|
-
fill(value: Partial<{
|
|
46
|
-
$attachments: AttributeOfModelWithAttachment;
|
|
47
|
-
}>, allowExtraProperties?: boolean): /*elided*/ any;
|
|
48
|
-
merge(value: Partial<{
|
|
49
|
-
$attachments: AttributeOfModelWithAttachment;
|
|
50
|
-
}>, allowExtraProperties?: boolean): /*elided*/ any;
|
|
51
|
-
isDirty(fields?: "$attachments" | ("$attachments" | undefined)[] | undefined): boolean;
|
|
52
|
-
enableForceUpdate(): /*elided*/ any;
|
|
53
|
-
save(): Promise</*elided*/ any>;
|
|
54
|
-
lockForUpdate<T_1>(callback: (user: /*elided*/ any) => T_1 | Promise<T_1>): Promise<T_1>;
|
|
55
|
-
delete(): Promise<void>;
|
|
56
|
-
refresh(): Promise</*elided*/ any>;
|
|
57
|
-
load: import("@adonisjs/lucid/types/model").LucidRowPreload</*elided*/ any>;
|
|
58
|
-
loadOnce: import("@adonisjs/lucid/types/model").LucidRowPreloadOnce</*elided*/ any>;
|
|
59
|
-
preload: import("@adonisjs/lucid/types/model").LucidRowPreload</*elided*/ any>;
|
|
60
|
-
loadAggregate: <Self extends /*elided*/ any, Name extends import("@adonisjs/lucid/types/relations").ExtractModelRelations<Self>, RelatedBuilder = Self[Name] extends import("@adonisjs/lucid/types/relations").ModelRelations<import("@adonisjs/lucid/types/model").LucidModel, import("@adonisjs/lucid/types/model").LucidModel> ? Self[Name]["subQuery"] : never>(name: Name, callback: (builder: RelatedBuilder) => void) => import("@adonisjs/lucid/types/model").LazyLoadAggregatesContract<Self>;
|
|
61
|
-
loadCount: <Self extends /*elided*/ any, Name extends import("@adonisjs/lucid/types/relations").ExtractModelRelations<Self>, RelatedBuilder = Self[Name] extends import("@adonisjs/lucid/types/relations").ModelRelations<import("@adonisjs/lucid/types/model").LucidModel, import("@adonisjs/lucid/types/model").LucidModel> ? Self[Name]["subQuery"] : never>(name: Name, callback?: ((builder: RelatedBuilder) => void) | undefined) => import("@adonisjs/lucid/types/model").LazyLoadAggregatesContract<Self>;
|
|
62
|
-
serializeAttributes(fields?: import("@adonisjs/lucid/types/model").CherryPickFields, raw?: boolean): import("@adonisjs/lucid/types/model").ModelObject;
|
|
63
|
-
serializeComputed(fields?: import("@adonisjs/lucid/types/model").CherryPickFields): import("@adonisjs/lucid/types/model").ModelObject;
|
|
64
|
-
serializeRelations(fields: undefined, raw: true): {
|
|
65
|
-
[key: string]: import("@adonisjs/lucid/types/model").LucidRow | import("@adonisjs/lucid/types/model").LucidRow[];
|
|
66
|
-
};
|
|
67
|
-
serializeRelations(cherryPick: import("@adonisjs/lucid/types/model").CherryPick["relations"] | undefined, raw: false | undefined): import("@adonisjs/lucid/types/model").ModelObject;
|
|
68
|
-
serializeRelations(cherryPick?: import("@adonisjs/lucid/types/model").CherryPick["relations"], raw?: boolean): import("@adonisjs/lucid/types/model").ModelObject;
|
|
69
|
-
serialize(cherryPick?: import("@adonisjs/lucid/types/model").CherryPick): import("@adonisjs/lucid/types/model").ModelObject;
|
|
70
|
-
toObject(): import("@adonisjs/lucid/types/model").ModelObject;
|
|
71
|
-
toJSON(): import("@adonisjs/lucid/types/model").ModelObject;
|
|
72
|
-
related<Name extends undefined>(relation: Name): /*elided*/ any[Name] extends import("@adonisjs/lucid/types/relations").ModelRelations<import("@adonisjs/lucid/types/model").LucidModel, import("@adonisjs/lucid/types/model").LucidModel> ? /*elided*/ any[Name]["client"] : never;
|
|
73
|
-
};
|
|
74
|
-
warn(): Promise<void>;
|
|
75
|
-
find: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, value: any, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<null | InstanceType<T_1>>;
|
|
76
|
-
readonly booted: boolean;
|
|
77
|
-
boot: () => void;
|
|
78
|
-
after: {
|
|
79
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel>(this: Model, event: "fetch", handler: import("@adonisjs/lucid/types/model").HooksHandler<InstanceType<Model>[], "fetch">): void;
|
|
80
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel>(this: Model, event: "paginate", handler: import("@adonisjs/lucid/types/model").HooksHandler<import("@adonisjs/lucid/types/model").ModelPaginatorContract<InstanceType<Model>>, "paginate">): void;
|
|
81
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel, Event extends import("@adonisjs/lucid/types/model").EventsList>(this: Model, event: Event, handler: import("@adonisjs/lucid/types/model").HooksHandler<InstanceType<Model>, Event>): void;
|
|
82
|
-
};
|
|
83
|
-
create: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, values: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>, options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>>;
|
|
84
|
-
all: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T_1>[]>;
|
|
85
|
-
namingStrategy: import("@adonisjs/lucid/types/model").NamingStrategyContract;
|
|
86
|
-
$columnsDefinitions: Map<string, import("@adonisjs/lucid/types/model").ModelColumnOptions>;
|
|
87
|
-
$relationsDefinitions: Map<string, import("@adonisjs/lucid/types/relations").RelationshipsContract>;
|
|
88
|
-
$computedDefinitions: Map<string, import("@adonisjs/lucid/types/model").ComputedOptions>;
|
|
89
|
-
primaryKey: string;
|
|
90
|
-
connection?: string | undefined;
|
|
91
|
-
table: string;
|
|
92
|
-
selfAssignPrimaryKey: boolean;
|
|
93
|
-
$adapter: import("@adonisjs/lucid/types/model").AdapterContract;
|
|
94
|
-
useAdapter: (adapter: import("@adonisjs/lucid/types/model").AdapterContract) => void;
|
|
95
|
-
$hooks: import("@poppinss/hooks").default<any>;
|
|
96
|
-
$keys: {
|
|
97
|
-
attributesToColumns: import("@adonisjs/lucid/types/model").ModelKeysContract;
|
|
98
|
-
attributesToSerialized: import("@adonisjs/lucid/types/model").ModelKeysContract;
|
|
99
|
-
columnsToAttributes: import("@adonisjs/lucid/types/model").ModelKeysContract;
|
|
100
|
-
columnsToSerialized: import("@adonisjs/lucid/types/model").ModelKeysContract;
|
|
101
|
-
serializedToColumns: import("@adonisjs/lucid/types/model").ModelKeysContract;
|
|
102
|
-
serializedToAttributes: import("@adonisjs/lucid/types/model").ModelKeysContract;
|
|
103
|
-
};
|
|
104
|
-
$createFromAdapterResult: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, result?: import("@adonisjs/lucid/types/model").ModelObject, sideloadAttributes?: import("@adonisjs/lucid/types/model").ModelObject, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => null | InstanceType<T_1>;
|
|
105
|
-
$createMultipleFromAdapterResult: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, results: import("@adonisjs/lucid/types/model").ModelObject[], sideloadAttributes?: import("@adonisjs/lucid/types/model").ModelObject, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => InstanceType<T_1>[];
|
|
106
|
-
$addColumn: (name: string, options: Partial<import("@adonisjs/lucid/types/model").ColumnOptions>) => import("@adonisjs/lucid/types/model").ColumnOptions;
|
|
107
|
-
$hasColumn: (name: string) => boolean;
|
|
108
|
-
$getColumn: (name: string) => import("@adonisjs/lucid/types/model").ModelColumnOptions | undefined;
|
|
109
|
-
$addComputed: (name: string, options: Partial<import("@adonisjs/lucid/types/model").ComputedOptions>) => import("@adonisjs/lucid/types/model").ComputedOptions;
|
|
110
|
-
$hasComputed: (name: string) => boolean;
|
|
111
|
-
$getComputed: (name: string) => import("@adonisjs/lucid/types/model").ComputedOptions | undefined;
|
|
112
|
-
$addRelation: (name: string, type: import("@adonisjs/lucid/types/relations").ModelRelationTypes["__opaque_type"], relatedModel: () => import("@adonisjs/lucid/types/model").LucidModel, options: import("@adonisjs/lucid/types/model").ModelRelationOptions) => void;
|
|
113
|
-
$hasRelation: (name: string) => boolean;
|
|
114
|
-
$getRelation: {
|
|
115
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel, Name extends import("@adonisjs/lucid/types/relations").ExtractModelRelations<InstanceType<Model>>>(this: Model, name: Name): InstanceType<Model>[Name] extends import("@adonisjs/lucid/types/relations").ModelRelations<import("@adonisjs/lucid/types/model").LucidModel, import("@adonisjs/lucid/types/model").LucidModel> ? InstanceType<Model>[Name]["client"]["relation"] : import("@adonisjs/lucid/types/relations").RelationshipsContract;
|
|
116
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel>(this: Model, name: string): import("@adonisjs/lucid/types/relations").RelationshipsContract;
|
|
117
|
-
};
|
|
118
|
-
$defineProperty: <Model extends import("@adonisjs/lucid/types/model").LucidModel, Prop extends keyof Model>(this: Model, propertyName: Prop, defaultValue: Model[Prop], strategy: "inherit" | "define" | ((value: Model[Prop]) => Model[Prop])) => void;
|
|
119
|
-
before: {
|
|
120
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel, Event extends "find" | "fetch">(this: Model, event: Event, handler: import("@adonisjs/lucid/types/model").HooksHandler<import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<Model>, Event>): void;
|
|
121
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel>(this: Model, event: "paginate", handler: import("@adonisjs/lucid/types/model").HooksHandler<[import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<Model>, import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<Model>], "paginate">): void;
|
|
122
|
-
<Model extends import("@adonisjs/lucid/types/model").LucidModel, Event extends import("@adonisjs/lucid/types/model").EventsList>(this: Model, event: Event, handler: import("@adonisjs/lucid/types/model").HooksHandler<InstanceType<Model>, Event>): void;
|
|
123
|
-
};
|
|
124
|
-
createMany: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, values: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>[], options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>[]>;
|
|
125
|
-
findOrFail: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, value: any, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T_1>>;
|
|
126
|
-
findBy: {
|
|
127
|
-
<T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, clause: Record<string, unknown>, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<null | InstanceType<T_1>>;
|
|
128
|
-
<T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, key: string, value: any, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<null | InstanceType<T_1>>;
|
|
129
|
-
};
|
|
130
|
-
findByOrFail: {
|
|
131
|
-
<T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, clause: Record<string, unknown>, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<InstanceType<T_1>>;
|
|
132
|
-
<T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, key: string, value: any, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<InstanceType<T_1>>;
|
|
133
|
-
};
|
|
134
|
-
findManyBy: {
|
|
135
|
-
<T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, clause: Record<string, unknown>, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<InstanceType<T_1>[]>;
|
|
136
|
-
<T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, key: string, value: any, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<InstanceType<T_1>[]>;
|
|
137
|
-
};
|
|
138
|
-
first: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<null | InstanceType<T_1>>;
|
|
139
|
-
firstOrFail: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T_1>>;
|
|
140
|
-
findMany: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, value: any[], options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T_1>[]>;
|
|
141
|
-
firstOrNew: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, searchPayload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>, savePayload?: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>, options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>>;
|
|
142
|
-
firstOrCreate: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, searchPayload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>, savePayload?: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>, options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>>;
|
|
143
|
-
updateOrCreate: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, searchPayload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>, updatePayload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>, options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>>;
|
|
144
|
-
fetchOrNewUpMany: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, predicate: keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>> | (keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>)[], payload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>[], options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>[]>;
|
|
145
|
-
fetchOrCreateMany: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, predicate: keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>> | (keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>)[], payload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>[], options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>[]>;
|
|
146
|
-
updateOrCreateMany: <T_1 extends import("@adonisjs/lucid/types/model").LucidModel>(this: T_1, predicate: keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>> | (keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>)[], payload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T_1>>>[], options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T_1>[]>;
|
|
147
|
-
query: <Model extends import("@adonisjs/lucid/types/model").LucidModel, Result = InstanceType<Model>>(this: Model, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<Model, Result>;
|
|
148
|
-
transaction: import("@adonisjs/lucid/types/database").TransactionFn;
|
|
149
|
-
truncate: (cascade?: boolean) => Promise<void>;
|
|
150
|
-
} & T;
|
|
151
|
-
export {};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @jrmc/adonis-attachment
|
|
3
|
-
*
|
|
4
|
-
* @license MIT
|
|
5
|
-
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
-
*/
|
|
7
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
8
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
10
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
11
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
12
|
-
};
|
|
13
|
-
import { beforeSave, beforeFind, beforeFetch, beforePaginate, beforeCreate, } from '@adonisjs/lucid/orm';
|
|
14
|
-
import { defaultStateAttributeMixin } from '../utils/default_values.js';
|
|
15
|
-
import logger from '@adonisjs/core/services/logger';
|
|
16
|
-
export function Attachmentable(superclass) {
|
|
17
|
-
class ModelWithAttachment extends superclass {
|
|
18
|
-
$attachments = structuredClone(defaultStateAttributeMixin);
|
|
19
|
-
static async warn() {
|
|
20
|
-
logger.warn(`The "Attachmentable" mixin is deprecated and may be removed in a future version.`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
__decorate([
|
|
24
|
-
beforeCreate(),
|
|
25
|
-
beforeFind(),
|
|
26
|
-
beforeFetch(),
|
|
27
|
-
beforePaginate(),
|
|
28
|
-
beforeSave()
|
|
29
|
-
], ModelWithAttachment, "warn", null);
|
|
30
|
-
return ModelWithAttachment;
|
|
31
|
-
}
|