@strapi/core 5.53.0 → 5.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/content-type/index.d.ts.map +1 -1
- package/dist/domain/content-type/index.js +3 -1
- package/dist/domain/content-type/index.js.map +1 -1
- package/dist/domain/content-type/index.mjs +3 -1
- package/dist/domain/content-type/index.mjs.map +1 -1
- package/dist/package.json.js +14 -15
- package/dist/package.json.js.map +1 -1
- package/dist/package.json.mjs +14 -15
- package/dist/package.json.mjs.map +1 -1
- package/dist/services/content-source-maps.d.ts +14 -2
- package/dist/services/content-source-maps.d.ts.map +1 -1
- package/dist/services/content-source-maps.js +134 -1
- package/dist/services/content-source-maps.js.map +1 -1
- package/dist/services/content-source-maps.mjs +134 -2
- package/dist/services/content-source-maps.mjs.map +1 -1
- package/dist/services/cron.d.ts +3 -18
- package/dist/services/cron.d.ts.map +1 -1
- package/dist/services/cron.js +241 -33
- package/dist/services/cron.js.map +1 -1
- package/dist/services/cron.mjs +241 -33
- package/dist/services/cron.mjs.map +1 -1
- package/dist/services/document-service/transform/relations/extract/data-ids.d.ts +1 -1
- package/dist/services/document-service/transform/relations/extract/data-ids.d.ts.map +1 -1
- package/dist/services/document-service/transform/relations/extract/data-ids.js +12 -3
- package/dist/services/document-service/transform/relations/extract/data-ids.js.map +1 -1
- package/dist/services/document-service/transform/relations/extract/data-ids.mjs +12 -3
- package/dist/services/document-service/transform/relations/extract/data-ids.mjs.map +1 -1
- package/dist/services/document-service/transform/relations/transform/data-ids.d.ts +1 -1
- package/dist/services/document-service/transform/relations/transform/data-ids.d.ts.map +1 -1
- package/dist/services/document-service/transform/relations/transform/data-ids.js +12 -3
- package/dist/services/document-service/transform/relations/transform/data-ids.js.map +1 -1
- package/dist/services/document-service/transform/relations/transform/data-ids.mjs +12 -3
- package/dist/services/document-service/transform/relations/transform/data-ids.mjs.map +1 -1
- package/dist/services/document-service/transform/relations/utils/map-relation.d.ts +6 -1
- package/dist/services/document-service/transform/relations/utils/map-relation.d.ts.map +1 -1
- package/dist/services/document-service/transform/relations/utils/map-relation.js +13 -10
- package/dist/services/document-service/transform/relations/utils/map-relation.js.map +1 -1
- package/dist/services/document-service/transform/relations/utils/map-relation.mjs +13 -10
- package/dist/services/document-service/transform/relations/utils/map-relation.mjs.map +1 -1
- package/dist/services/features.d.ts.map +1 -1
- package/dist/services/features.js +3 -0
- package/dist/services/features.js.map +1 -1
- package/dist/services/features.mjs +3 -0
- package/dist/services/features.mjs.map +1 -1
- package/package.json +14 -15
|
@@ -32,9 +32,124 @@ const EXCLUDED_FIELDS = [
|
|
|
32
32
|
const isObject = (value)=>{
|
|
33
33
|
return typeof value === 'object' && value !== null;
|
|
34
34
|
};
|
|
35
|
+
const blocksFieldMetadata = (metadata)=>({
|
|
36
|
+
...metadata,
|
|
37
|
+
path: metadata.fieldPath,
|
|
38
|
+
type: 'blocks'
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Walk a subtree depth-first and stega-encode the first `{ type: 'text', text }` leaf only.
|
|
42
|
+
*/ const encodeFirstTextLeaf = (node, metadata, encodeField)=>{
|
|
43
|
+
if (!isObject(node)) {
|
|
44
|
+
return {
|
|
45
|
+
node,
|
|
46
|
+
encoded: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (node.type === 'text' && typeof node.text === 'string') {
|
|
50
|
+
return {
|
|
51
|
+
node: {
|
|
52
|
+
...node,
|
|
53
|
+
text: encodeField(node.text, blocksFieldMetadata(metadata))
|
|
54
|
+
},
|
|
55
|
+
encoded: true
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(node.children)) {
|
|
59
|
+
let encoded = false;
|
|
60
|
+
const children = node.children.map((child)=>{
|
|
61
|
+
if (encoded) {
|
|
62
|
+
return child;
|
|
63
|
+
}
|
|
64
|
+
const result = encodeFirstTextLeaf(child, metadata, encodeField);
|
|
65
|
+
encoded = result.encoded;
|
|
66
|
+
return result.node;
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
node: {
|
|
70
|
+
...node,
|
|
71
|
+
children
|
|
72
|
+
},
|
|
73
|
+
encoded
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
node,
|
|
78
|
+
encoded: false
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const encodeListBlock = (listNode, metadata, encodeField)=>{
|
|
82
|
+
const children = listNode.children.map((child)=>{
|
|
83
|
+
if (!isObject(child)) {
|
|
84
|
+
return child;
|
|
85
|
+
}
|
|
86
|
+
if (child.type === 'list-item') {
|
|
87
|
+
return encodeFirstTextLeaf(child, metadata, encodeField).node;
|
|
88
|
+
}
|
|
89
|
+
if (child.type === 'list') {
|
|
90
|
+
return encodeListBlock(child, metadata, encodeField);
|
|
91
|
+
}
|
|
92
|
+
return child;
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
...listNode,
|
|
96
|
+
children
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const encodeImageBlock = (imageNode, metadata, encodeField)=>{
|
|
100
|
+
if (!isObject(imageNode.image)) {
|
|
101
|
+
return imageNode;
|
|
102
|
+
}
|
|
103
|
+
const { alternativeText } = imageNode.image;
|
|
104
|
+
// Encode the alt text (not the URL). Encoding the URL corrupts the src attribute and can
|
|
105
|
+
// cause the preview highlight to expand across the entire page. The alt attribute is a
|
|
106
|
+
// safe target: it's visible to the previewScript via getAttribute and never used as a URL.
|
|
107
|
+
// Only encode when alternativeText is a non-empty string — images with no alt text get
|
|
108
|
+
// no stega marker and won't independently trigger the highlight, but the blocks field
|
|
109
|
+
// as a whole remains highlightable via its other (text) blocks.
|
|
110
|
+
if (typeof alternativeText !== 'string' || alternativeText.length === 0) {
|
|
111
|
+
return imageNode;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
...imageNode,
|
|
115
|
+
image: {
|
|
116
|
+
...imageNode.image,
|
|
117
|
+
alternativeText: encodeField(alternativeText, blocksFieldMetadata(metadata))
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Pure transformation over a blocks AST. Injects one stega marker per visual block
|
|
123
|
+
* (first text leaf, or image alt text) using the blocks field path for all markers.
|
|
124
|
+
* Code blocks are skipped entirely.
|
|
125
|
+
*/ const encodeBlocks = (blocks, metadata, encodeField)=>{
|
|
126
|
+
if (!Array.isArray(blocks)) {
|
|
127
|
+
return blocks;
|
|
128
|
+
}
|
|
129
|
+
return blocks.map((block)=>{
|
|
130
|
+
if (!isObject(block)) {
|
|
131
|
+
return block;
|
|
132
|
+
}
|
|
133
|
+
switch(block.type){
|
|
134
|
+
case 'code':
|
|
135
|
+
// Skip encoding — encoding code content would corrupt the syntax.
|
|
136
|
+
return block;
|
|
137
|
+
case 'image':
|
|
138
|
+
return encodeImageBlock(block, metadata, encodeField);
|
|
139
|
+
case 'list':
|
|
140
|
+
return encodeListBlock(block, metadata, encodeField);
|
|
141
|
+
case 'paragraph':
|
|
142
|
+
case 'heading':
|
|
143
|
+
case 'quote':
|
|
144
|
+
return encodeFirstTextLeaf(block, metadata, encodeField).node;
|
|
145
|
+
default:
|
|
146
|
+
return block;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
};
|
|
35
150
|
const createContentSourceMapsService = (strapi)=>{
|
|
36
151
|
return {
|
|
37
|
-
encodeField (text, { kind, model, documentId, type, path, locale }) {
|
|
152
|
+
encodeField (text, { kind, model, documentId, type, path, locale, fieldPath }) {
|
|
38
153
|
/**
|
|
39
154
|
* Combine all metadata into into a one string so we only have to deal with one data-atribute
|
|
40
155
|
* on the frontend. Make it human readable because that data-attribute may be set manually by
|
|
@@ -52,11 +167,17 @@ const createContentSourceMapsService = (strapi)=>{
|
|
|
52
167
|
if (locale) {
|
|
53
168
|
strapiSource.set('locale', locale);
|
|
54
169
|
}
|
|
170
|
+
if (fieldPath) {
|
|
171
|
+
strapiSource.set('fieldPath', fieldPath);
|
|
172
|
+
}
|
|
55
173
|
const encoded = vercelStegaCombine(text, {
|
|
56
174
|
strapiSource: strapiSource.toString()
|
|
57
175
|
}, false);
|
|
58
176
|
return encoded;
|
|
59
177
|
},
|
|
178
|
+
encodeBlocks (blocks, metadata) {
|
|
179
|
+
return encodeBlocks(blocks, metadata, this.encodeField.bind(this));
|
|
180
|
+
},
|
|
60
181
|
async encodeEntry ({ data, schema }) {
|
|
61
182
|
if (!isObject(data) || data === undefined) {
|
|
62
183
|
return data;
|
|
@@ -65,6 +186,17 @@ const createContentSourceMapsService = (strapi)=>{
|
|
|
65
186
|
if (!attribute || EXCLUDED_FIELDS.includes(key)) {
|
|
66
187
|
return;
|
|
67
188
|
}
|
|
189
|
+
if (attribute.type === 'blocks' && Array.isArray(value)) {
|
|
190
|
+
const fieldPath = path.rawWithIndices;
|
|
191
|
+
set(key, this.encodeBlocks(value, {
|
|
192
|
+
fieldPath,
|
|
193
|
+
kind: schema.kind,
|
|
194
|
+
model: schema.uid,
|
|
195
|
+
locale: data.locale,
|
|
196
|
+
documentId: data.documentId
|
|
197
|
+
}));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
68
200
|
if (ENCODABLE_TYPES.includes(attribute.type) && typeof value === 'string') {
|
|
69
201
|
// For inner fields of a multi-media field's items (e.g. `medias.0.url`),
|
|
70
202
|
// drop the array index so all items share the same encoded path. The
|
|
@@ -110,5 +242,5 @@ const createContentSourceMapsService = (strapi)=>{
|
|
|
110
242
|
};
|
|
111
243
|
};
|
|
112
244
|
|
|
113
|
-
export { createContentSourceMapsService };
|
|
245
|
+
export { createContentSourceMapsService, encodeBlocks };
|
|
114
246
|
//# sourceMappingURL=content-source-maps.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-source-maps.mjs","sources":["../../src/services/content-source-maps.ts"],"sourcesContent":["import { vercelStegaCombine } from '@vercel/stega';\nimport type { Core, Struct, UID } from '@strapi/types';\nimport { traverseEntity } from '@strapi/utils';\nimport type { FieldContentSourceMap } from '@strapi/admin/strapi-admin';\n\nconst ENCODABLE_TYPES = [\n 'string',\n 'text',\n 'richtext',\n 'biginteger',\n 'date',\n 'time',\n 'datetime',\n 'timestamp',\n 'boolean',\n 'enumeration',\n 'json',\n 'media',\n 'email',\n 'password',\n /**\n * We cannot modify the response shape, so types that aren't based on string cannot be encoded:\n * - json: object\n * - blocks: object, will require a custom implementation in a dedicated PR\n * - integer, float and decimal: number\n * - boolean: boolean (believe it or not)\n * - uid: can be stringified but would mess up URLs\n */\n];\n\n// TODO: use a centralized store for these fields that would be shared with the CM and CTB\nconst EXCLUDED_FIELDS = [\n 'id',\n 'documentId',\n 'locale',\n 'localizations',\n 'created_by',\n 'updated_by',\n 'created_at',\n 'updated_at',\n 'publishedAt',\n];\n\ninterface EncodingInfo {\n data: any;\n schema: Struct.Schema;\n}\n\nconst isObject = (value: unknown): value is Record<string, any> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst createContentSourceMapsService = (strapi: Core.Strapi) => {\n return {\n encodeField(\n text: string,\n { kind, model, documentId, type, path, locale }: FieldContentSourceMap\n ) {\n /**\n * Combine all metadata into into a one string so we only have to deal with one data-atribute\n * on the frontend. Make it human readable because that data-attribute may be set manually by\n * users for fields that don't support sourcemap encoding.\n */\n const strapiSource = new URLSearchParams();\n strapiSource.set('documentId', documentId);\n strapiSource.set('type', type);\n strapiSource.set('path', path);\n\n if (model) {\n strapiSource.set('model', model);\n }\n if (kind) {\n strapiSource.set('kind', kind);\n }\n if (locale) {\n strapiSource.set('locale', locale);\n }\n\n const encoded = vercelStegaCombine(\n text,\n {\n strapiSource: strapiSource.toString(),\n },\n false\n );\n\n return encoded;\n },\n\n async encodeEntry({ data, schema }: EncodingInfo): Promise<any> {\n if (!isObject(data) || data === undefined) {\n return data;\n }\n\n return traverseEntity(\n ({ key, value, attribute, schema, path, parent }, { set }) => {\n if (!attribute || EXCLUDED_FIELDS.includes(key)) {\n return;\n }\n\n if (ENCODABLE_TYPES.includes(attribute.type) && typeof value === 'string') {\n // For inner fields of a multi-media field's items (e.g. `medias.0.url`),\n // drop the array index so all items share the same encoded path. The\n // preview groups them under one highlight and opens the multi-media\n // input as a single field, matching the side editor.\n const parentAttr = parent?.attribute;\n const isInsideMultiMedia =\n parentAttr?.type === 'media' && (parentAttr as any).multiple === true;\n const encodedPath =\n isInsideMultiMedia && parent?.path?.rawWithIndices\n ? `${parent.path.rawWithIndices}.${key}`\n : path.rawWithIndices!;\n\n set(\n key,\n this.encodeField(value, {\n path: encodedPath,\n type: attribute.type,\n kind: schema.kind,\n model: schema.uid as UID.Schema,\n locale: data.locale,\n documentId: data.documentId,\n }) as any\n );\n }\n },\n {\n schema,\n getModel: (uid) => strapi.getModel(uid as UID.Schema),\n },\n data\n );\n },\n\n async encodeSourceMaps({ data, schema }: EncodingInfo): Promise<any> {\n try {\n if (Array.isArray(data)) {\n return await Promise.all(\n data.map((item) => this.encodeSourceMaps({ data: item, schema }))\n );\n }\n\n if (!isObject(data)) {\n return data;\n }\n\n return await this.encodeEntry({ data, schema });\n } catch (error) {\n strapi.log.error('Error encoding source maps:', error);\n return data;\n }\n },\n };\n};\n\nexport { createContentSourceMapsService };\n"],"names":["ENCODABLE_TYPES","EXCLUDED_FIELDS","isObject","value","createContentSourceMapsService","strapi","encodeField","text","kind","model","documentId","type","path","locale","strapiSource","URLSearchParams","set","encoded","vercelStegaCombine","toString","encodeEntry","data","schema","undefined","traverseEntity","key","attribute","parent","includes","parentAttr","isInsideMultiMedia","multiple","encodedPath","rawWithIndices","uid","getModel","encodeSourceMaps","Array","isArray","Promise","all","map","item","error","log"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"content-source-maps.mjs","sources":["../../src/services/content-source-maps.ts"],"sourcesContent":["import { vercelStegaCombine } from '@vercel/stega';\nimport type { Core, Struct, UID } from '@strapi/types';\nimport { traverseEntity } from '@strapi/utils';\nimport type { FieldContentSourceMap } from '@strapi/admin/strapi-admin';\n\nconst ENCODABLE_TYPES = [\n 'string',\n 'text',\n 'richtext',\n 'biginteger',\n 'date',\n 'time',\n 'datetime',\n 'timestamp',\n 'boolean',\n 'enumeration',\n 'json',\n 'media',\n 'email',\n 'password',\n /**\n * We cannot modify the response shape, so types that aren't based on string cannot be encoded:\n * - json: object\n * - integer, float and decimal: number\n * - boolean: boolean (believe it or not)\n * - uid: can be stringified but would mess up URLs\n *\n * The blocks type is an array of nodes — handled in a dedicated branch below\n * because it requires walking the AST to encode the first text leaf of each visual block.\n */\n];\n\n// TODO: use a centralized store for these fields that would be shared with the CM and CTB\nconst EXCLUDED_FIELDS = [\n 'id',\n 'documentId',\n 'locale',\n 'localizations',\n 'created_by',\n 'updated_by',\n 'created_at',\n 'updated_at',\n 'publishedAt',\n];\n\ninterface EncodingInfo {\n data: any;\n schema: Struct.Schema;\n}\n\ntype BlocksEncodeMetadata = Omit<FieldContentSourceMap, 'path' | 'type'> & { fieldPath: string };\n\ntype EncodeFieldFn = (text: string, metadata: FieldContentSourceMap) => string;\n\nconst isObject = (value: unknown): value is Record<string, any> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst blocksFieldMetadata = (metadata: BlocksEncodeMetadata): FieldContentSourceMap => ({\n ...metadata,\n path: metadata.fieldPath,\n type: 'blocks',\n});\n\n/**\n * Walk a subtree depth-first and stega-encode the first `{ type: 'text', text }` leaf only.\n */\nconst encodeFirstTextLeaf = (\n node: unknown,\n metadata: BlocksEncodeMetadata,\n encodeField: EncodeFieldFn\n): { node: unknown; encoded: boolean } => {\n if (!isObject(node)) {\n return { node, encoded: false };\n }\n\n if (node.type === 'text' && typeof node.text === 'string') {\n return {\n node: {\n ...node,\n text: encodeField(node.text, blocksFieldMetadata(metadata)),\n },\n encoded: true,\n };\n }\n\n if (Array.isArray(node.children)) {\n let encoded = false;\n const children = node.children.map((child) => {\n if (encoded) {\n return child;\n }\n\n const result = encodeFirstTextLeaf(child, metadata, encodeField);\n encoded = result.encoded;\n return result.node;\n });\n\n return {\n node: { ...node, children },\n encoded,\n };\n }\n\n return { node, encoded: false };\n};\n\nconst encodeListBlock = (\n listNode: Record<string, unknown>,\n metadata: BlocksEncodeMetadata,\n encodeField: EncodeFieldFn\n): Record<string, unknown> => {\n const children = (listNode.children as unknown[]).map((child) => {\n if (!isObject(child)) {\n return child;\n }\n\n if (child.type === 'list-item') {\n return encodeFirstTextLeaf(child, metadata, encodeField).node;\n }\n\n if (child.type === 'list') {\n return encodeListBlock(child, metadata, encodeField);\n }\n\n return child;\n });\n\n return { ...listNode, children };\n};\n\nconst encodeImageBlock = (\n imageNode: Record<string, unknown>,\n metadata: BlocksEncodeMetadata,\n encodeField: EncodeFieldFn\n): Record<string, unknown> => {\n if (!isObject(imageNode.image)) {\n return imageNode;\n }\n\n const { alternativeText } = imageNode.image as Record<string, unknown>;\n // Encode the alt text (not the URL). Encoding the URL corrupts the src attribute and can\n // cause the preview highlight to expand across the entire page. The alt attribute is a\n // safe target: it's visible to the previewScript via getAttribute and never used as a URL.\n // Only encode when alternativeText is a non-empty string — images with no alt text get\n // no stega marker and won't independently trigger the highlight, but the blocks field\n // as a whole remains highlightable via its other (text) blocks.\n if (typeof alternativeText !== 'string' || alternativeText.length === 0) {\n return imageNode;\n }\n\n return {\n ...imageNode,\n image: {\n ...(imageNode.image as Record<string, unknown>),\n alternativeText: encodeField(alternativeText, blocksFieldMetadata(metadata)),\n },\n };\n};\n\n/**\n * Pure transformation over a blocks AST. Injects one stega marker per visual block\n * (first text leaf, or image alt text) using the blocks field path for all markers.\n * Code blocks are skipped entirely.\n */\nconst encodeBlocks = (\n blocks: unknown,\n metadata: BlocksEncodeMetadata,\n encodeField: EncodeFieldFn\n): unknown => {\n if (!Array.isArray(blocks)) {\n return blocks;\n }\n\n return blocks.map((block) => {\n if (!isObject(block)) {\n return block;\n }\n\n switch (block.type) {\n case 'code':\n // Skip encoding — encoding code content would corrupt the syntax.\n return block;\n case 'image':\n return encodeImageBlock(block, metadata, encodeField);\n case 'list':\n return encodeListBlock(block, metadata, encodeField);\n case 'paragraph':\n case 'heading':\n case 'quote':\n return encodeFirstTextLeaf(block, metadata, encodeField).node;\n default:\n return block;\n }\n });\n};\n\nconst createContentSourceMapsService = (strapi: Core.Strapi) => {\n return {\n encodeField(\n text: string,\n { kind, model, documentId, type, path, locale, fieldPath }: FieldContentSourceMap\n ) {\n /**\n * Combine all metadata into into a one string so we only have to deal with one data-atribute\n * on the frontend. Make it human readable because that data-attribute may be set manually by\n * users for fields that don't support sourcemap encoding.\n */\n const strapiSource = new URLSearchParams();\n strapiSource.set('documentId', documentId);\n strapiSource.set('type', type);\n strapiSource.set('path', path);\n\n if (model) {\n strapiSource.set('model', model);\n }\n if (kind) {\n strapiSource.set('kind', kind);\n }\n if (locale) {\n strapiSource.set('locale', locale);\n }\n if (fieldPath) {\n strapiSource.set('fieldPath', fieldPath);\n }\n\n const encoded = vercelStegaCombine(\n text,\n {\n strapiSource: strapiSource.toString(),\n },\n false\n );\n\n return encoded;\n },\n\n encodeBlocks(blocks: unknown, metadata: BlocksEncodeMetadata): unknown {\n return encodeBlocks(blocks, metadata, this.encodeField.bind(this));\n },\n\n async encodeEntry({ data, schema }: EncodingInfo): Promise<any> {\n if (!isObject(data) || data === undefined) {\n return data;\n }\n\n return traverseEntity(\n ({ key, value, attribute, schema, path, parent }, { set }) => {\n if (!attribute || EXCLUDED_FIELDS.includes(key)) {\n return;\n }\n\n if (attribute.type === 'blocks' && Array.isArray(value)) {\n const fieldPath = path.rawWithIndices!;\n set(\n key,\n this.encodeBlocks(value, {\n fieldPath,\n kind: schema.kind,\n model: schema.uid as UID.Schema,\n locale: data.locale,\n documentId: data.documentId,\n }) as any\n );\n return;\n }\n\n if (ENCODABLE_TYPES.includes(attribute.type) && typeof value === 'string') {\n // For inner fields of a multi-media field's items (e.g. `medias.0.url`),\n // drop the array index so all items share the same encoded path. The\n // preview groups them under one highlight and opens the multi-media\n // input as a single field, matching the side editor.\n const parentAttr = parent?.attribute;\n const isInsideMultiMedia =\n parentAttr?.type === 'media' && (parentAttr as any).multiple === true;\n const encodedPath =\n isInsideMultiMedia && parent?.path?.rawWithIndices\n ? `${parent.path.rawWithIndices}.${key}`\n : path.rawWithIndices!;\n\n set(\n key,\n this.encodeField(value, {\n path: encodedPath,\n type: attribute.type,\n kind: schema.kind,\n model: schema.uid as UID.Schema,\n locale: data.locale,\n documentId: data.documentId,\n }) as any\n );\n }\n },\n {\n schema,\n getModel: (uid) => strapi.getModel(uid as UID.Schema),\n },\n data\n );\n },\n\n async encodeSourceMaps({ data, schema }: EncodingInfo): Promise<any> {\n try {\n if (Array.isArray(data)) {\n return await Promise.all(\n data.map((item) => this.encodeSourceMaps({ data: item, schema }))\n );\n }\n\n if (!isObject(data)) {\n return data;\n }\n\n return await this.encodeEntry({ data, schema });\n } catch (error) {\n strapi.log.error('Error encoding source maps:', error);\n return data;\n }\n },\n };\n};\n\nexport { createContentSourceMapsService, encodeBlocks };\nexport type { BlocksEncodeMetadata, EncodeFieldFn };\n"],"names":["ENCODABLE_TYPES","EXCLUDED_FIELDS","isObject","value","blocksFieldMetadata","metadata","path","fieldPath","type","encodeFirstTextLeaf","node","encodeField","encoded","text","Array","isArray","children","map","child","result","encodeListBlock","listNode","encodeImageBlock","imageNode","image","alternativeText","length","encodeBlocks","blocks","block","createContentSourceMapsService","strapi","kind","model","documentId","locale","strapiSource","URLSearchParams","set","vercelStegaCombine","toString","bind","encodeEntry","data","schema","undefined","traverseEntity","key","attribute","parent","includes","rawWithIndices","uid","parentAttr","isInsideMultiMedia","multiple","encodedPath","getModel","encodeSourceMaps","Promise","all","item","error","log"],"mappings":"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"}
|
package/dist/services/cron.d.ts
CHANGED
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
interface JobSpec {
|
|
4
|
-
job: Job;
|
|
5
|
-
options: Spec;
|
|
6
|
-
name: string | null;
|
|
7
|
-
}
|
|
8
|
-
type TaskFn = ({ strapi }: {
|
|
9
|
-
strapi: Core.Strapi;
|
|
10
|
-
}, ...args: unknown[]) => Promise<unknown>;
|
|
11
|
-
type Task = TaskFn | {
|
|
12
|
-
task: TaskFn;
|
|
13
|
-
options: Spec;
|
|
14
|
-
};
|
|
15
|
-
interface Tasks {
|
|
16
|
-
[key: string]: Task;
|
|
17
|
-
}
|
|
1
|
+
import type { Modules } from '@strapi/types';
|
|
2
|
+
type Tasks = Modules.Cron.CronTasks;
|
|
18
3
|
declare const createCronService: () => {
|
|
19
4
|
add(tasks?: Tasks): /*elided*/ any;
|
|
20
5
|
remove(name: string): /*elided*/ any;
|
|
21
6
|
start(): /*elided*/ any;
|
|
22
7
|
stop(): /*elided*/ any;
|
|
23
8
|
destroy(): /*elided*/ any;
|
|
24
|
-
jobs: JobSpec[];
|
|
9
|
+
readonly jobs: Modules.Cron.JobSpec[];
|
|
25
10
|
};
|
|
26
11
|
export default createCronService;
|
|
27
12
|
//# sourceMappingURL=cron.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../../src/services/cron.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../../src/services/cron.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAS7C,KAAK,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;AA2JpC,QAAA,MAAM,iBAAiB;gBAoGR,KAAK;iBAuCH,MAAM;;;;;CAqCtB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
package/dist/services/cron.js
CHANGED
|
@@ -2,18 +2,228 @@
|
|
|
2
2
|
|
|
3
3
|
var fp = require('lodash/fp');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (!lazyNs) {
|
|
5
|
+
let lazyCroner;
|
|
6
|
+
const getCroner = ()=>{
|
|
7
|
+
if (!lazyCroner) {
|
|
9
8
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
10
|
-
|
|
9
|
+
lazyCroner = require('croner');
|
|
11
10
|
}
|
|
12
|
-
return
|
|
11
|
+
return lazyCroner;
|
|
12
|
+
};
|
|
13
|
+
const RECURRENCE_KEYS = [
|
|
14
|
+
'date',
|
|
15
|
+
'dayOfWeek',
|
|
16
|
+
'hour',
|
|
17
|
+
'minute',
|
|
18
|
+
'month',
|
|
19
|
+
'second',
|
|
20
|
+
'year',
|
|
21
|
+
'recurs'
|
|
22
|
+
];
|
|
23
|
+
const isPlainObject = (value)=>typeof value === 'object' && value !== null && !(value instanceof Date);
|
|
24
|
+
const isRuleOptions = (value)=>isPlainObject(value) && 'rule' in value;
|
|
25
|
+
const isRecurrenceSpec = (value)=>isPlainObject(value) && !('rule' in value) && RECURRENCE_KEYS.some((key)=>key in value);
|
|
26
|
+
const toDate = (value)=>value instanceof Date ? value : new Date(value);
|
|
27
|
+
const toSegmentPart = (value, offset = 0)=>{
|
|
28
|
+
if (typeof value === 'number') {
|
|
29
|
+
return String(value + offset);
|
|
30
|
+
}
|
|
31
|
+
return typeof value === 'string' ? value : null;
|
|
32
|
+
};
|
|
33
|
+
const segmentToField = (segment, fallback = '*', offset = 0)=>{
|
|
34
|
+
if (segment == null) {
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
const part = toSegmentPart(segment, offset);
|
|
38
|
+
if (part !== null) {
|
|
39
|
+
return part;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(segment)) {
|
|
42
|
+
return segment.map((item)=>segmentToField(item, fallback, offset)).join(',');
|
|
43
|
+
}
|
|
44
|
+
if (isPlainObject(segment)) {
|
|
45
|
+
const start = toSegmentPart(segment.start ?? segment.from, offset);
|
|
46
|
+
const end = toSegmentPart(segment.end ?? segment.to, offset);
|
|
47
|
+
if (start !== null && end !== null) {
|
|
48
|
+
const range = `${start}-${end}`;
|
|
49
|
+
const step = toSegmentPart(segment.step);
|
|
50
|
+
return step === null ? range : `${range}/${step}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return fallback;
|
|
54
|
+
};
|
|
55
|
+
const recurrenceToCron = (spec)=>{
|
|
56
|
+
const second = segmentToField(spec.second, '0');
|
|
57
|
+
const minute = segmentToField(spec.minute);
|
|
58
|
+
const hour = segmentToField(spec.hour);
|
|
59
|
+
const date = segmentToField(spec.date);
|
|
60
|
+
const month = segmentToField(spec.month, '*', 1);
|
|
61
|
+
const dayOfWeek = segmentToField(spec.dayOfWeek);
|
|
62
|
+
const fields = `${second} ${minute} ${hour} ${date} ${month} ${dayOfWeek}`;
|
|
63
|
+
return spec.year == null ? fields : `${fields} ${segmentToField(spec.year)}`;
|
|
64
|
+
};
|
|
65
|
+
const toCronerOptions = ({ tz, start, end })=>{
|
|
66
|
+
const cronerOptions = {};
|
|
67
|
+
if (tz) {
|
|
68
|
+
cronerOptions.timezone = tz;
|
|
69
|
+
}
|
|
70
|
+
if (start != null) {
|
|
71
|
+
cronerOptions.startAt = toDate(start);
|
|
72
|
+
}
|
|
73
|
+
if (end != null) {
|
|
74
|
+
cronerOptions.stopAt = toDate(end);
|
|
75
|
+
}
|
|
76
|
+
return cronerOptions;
|
|
77
|
+
};
|
|
78
|
+
const ruleToPattern = (rule)=>{
|
|
79
|
+
if (typeof rule === 'number') {
|
|
80
|
+
return {
|
|
81
|
+
pattern: new Date(rule),
|
|
82
|
+
oneShot: true
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (rule instanceof Date) {
|
|
86
|
+
return {
|
|
87
|
+
pattern: rule,
|
|
88
|
+
oneShot: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (typeof rule === 'string') {
|
|
92
|
+
return {
|
|
93
|
+
pattern: rule,
|
|
94
|
+
oneShot: false
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (isRecurrenceSpec(rule)) {
|
|
98
|
+
return {
|
|
99
|
+
pattern: recurrenceToCron(rule),
|
|
100
|
+
oneShot: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
throw new Error('Unsupported cron rule');
|
|
104
|
+
};
|
|
105
|
+
const toCronerArgs = (options)=>{
|
|
106
|
+
if (typeof options === 'number') {
|
|
107
|
+
return {
|
|
108
|
+
pattern: new Date(options),
|
|
109
|
+
cronerOptions: {
|
|
110
|
+
maxRuns: 1
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (options instanceof Date) {
|
|
115
|
+
return {
|
|
116
|
+
pattern: options,
|
|
117
|
+
cronerOptions: {
|
|
118
|
+
maxRuns: 1
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (typeof options === 'string') {
|
|
123
|
+
return {
|
|
124
|
+
pattern: options,
|
|
125
|
+
cronerOptions: {}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (isRecurrenceSpec(options)) {
|
|
129
|
+
return {
|
|
130
|
+
pattern: recurrenceToCron(options),
|
|
131
|
+
cronerOptions: toCronerOptions(options)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (isRuleOptions(options)) {
|
|
135
|
+
const cronerOptions = toCronerOptions(options);
|
|
136
|
+
const { pattern, oneShot } = ruleToPattern(options.rule);
|
|
137
|
+
if (oneShot) {
|
|
138
|
+
cronerOptions.maxRuns = 1;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
pattern,
|
|
142
|
+
cronerOptions
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
throw new Error('Unsupported cron schedule');
|
|
13
146
|
};
|
|
14
147
|
const createCronService = ()=>{
|
|
15
148
|
let jobsSpecs = [];
|
|
16
149
|
let running = false;
|
|
150
|
+
const createRunner = (fn)=>{
|
|
151
|
+
let nextFireDate = null;
|
|
152
|
+
return {
|
|
153
|
+
async run (self) {
|
|
154
|
+
const fireDate = nextFireDate ?? self?.currentRun() ?? new Date();
|
|
155
|
+
nextFireDate = self?.nextRun(fireDate) ?? null;
|
|
156
|
+
await fn({
|
|
157
|
+
strapi
|
|
158
|
+
}, fireDate);
|
|
159
|
+
},
|
|
160
|
+
setNextRun (date) {
|
|
161
|
+
nextFireDate = date;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
const createCronJob = (pattern, cronerOptions, fn, jobLabel)=>{
|
|
166
|
+
const { Cron: CronCtor } = getCroner();
|
|
167
|
+
const runner = createRunner(fn);
|
|
168
|
+
const job = new CronCtor(pattern, {
|
|
169
|
+
paused: !running,
|
|
170
|
+
// cron-parser (via node-schedule) accepted numeric-prefix stepping
|
|
171
|
+
// like `5/15` as `5-59/15`. Croner 10 rejects it unless this is set.
|
|
172
|
+
sloppyRanges: true,
|
|
173
|
+
...cronerOptions,
|
|
174
|
+
catch (error) {
|
|
175
|
+
strapi.log.error(`Cron job "${jobLabel}" failed`, error);
|
|
176
|
+
}
|
|
177
|
+
}, runner.run);
|
|
178
|
+
runner.setNextRun(job.nextRun());
|
|
179
|
+
return job;
|
|
180
|
+
};
|
|
181
|
+
const attachHandle = (job, fn, jobLabel)=>{
|
|
182
|
+
let current = job;
|
|
183
|
+
let handle;
|
|
184
|
+
const invoke = async ()=>{
|
|
185
|
+
const fireDate = current.currentRun() ?? new Date();
|
|
186
|
+
return fn({
|
|
187
|
+
strapi
|
|
188
|
+
}, fireDate);
|
|
189
|
+
};
|
|
190
|
+
const cancel = ()=>{
|
|
191
|
+
current.stop();
|
|
192
|
+
return true;
|
|
193
|
+
};
|
|
194
|
+
const nextInvocation = ()=>current.nextRun();
|
|
195
|
+
const reschedule = (spec)=>{
|
|
196
|
+
try {
|
|
197
|
+
const idx = jobsSpecs.findIndex((jobSpec)=>jobSpec.job === handle);
|
|
198
|
+
if (idx === -1) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const { pattern, cronerOptions } = toCronerArgs(spec);
|
|
202
|
+
const next = createCronJob(pattern, cronerOptions, fn, jobLabel);
|
|
203
|
+
current.stop();
|
|
204
|
+
current = next;
|
|
205
|
+
jobsSpecs[idx] = {
|
|
206
|
+
...jobsSpecs[idx],
|
|
207
|
+
options: spec
|
|
208
|
+
};
|
|
209
|
+
return true;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
strapi.log.error(`Could not reschedule cron job "${jobLabel}"`, error);
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
handle = new Proxy(job, {
|
|
216
|
+
get (_target, property) {
|
|
217
|
+
if (property === 'invoke') return invoke;
|
|
218
|
+
if (property === 'cancel') return cancel;
|
|
219
|
+
if (property === 'nextInvocation') return nextInvocation;
|
|
220
|
+
if (property === 'reschedule') return reschedule;
|
|
221
|
+
const value = Reflect.get(current, property, current);
|
|
222
|
+
return typeof value === 'function' ? value.bind(current) : value;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
return handle;
|
|
226
|
+
};
|
|
17
227
|
return {
|
|
18
228
|
add (tasks = {}) {
|
|
19
229
|
for (const taskExpression of Object.keys(tasks)){
|
|
@@ -22,65 +232,63 @@ const createCronService = ()=>{
|
|
|
22
232
|
let options;
|
|
23
233
|
let taskName;
|
|
24
234
|
if (fp.isFunction(taskValue)) {
|
|
25
|
-
// don't use task name if key is the rule
|
|
26
235
|
taskName = null;
|
|
27
236
|
fn = taskValue.bind(tasks);
|
|
28
237
|
options = taskExpression;
|
|
29
238
|
} else if (fp.isFunction(taskValue.task)) {
|
|
30
|
-
// set task name if key is not the rule
|
|
31
239
|
taskName = taskExpression;
|
|
32
240
|
fn = taskValue.task.bind(taskValue);
|
|
33
241
|
options = taskValue.options;
|
|
34
242
|
} else {
|
|
35
243
|
throw new Error(`Could not schedule a cron job for "${taskExpression}": no function found.`);
|
|
36
244
|
}
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
name: taskName
|
|
49
|
-
});
|
|
50
|
-
if (running) {
|
|
51
|
-
job.schedule(options);
|
|
245
|
+
const jobLabel = taskName ?? taskExpression;
|
|
246
|
+
try {
|
|
247
|
+
const { pattern, cronerOptions } = toCronerArgs(options);
|
|
248
|
+
const job = createCronJob(pattern, cronerOptions, fn, jobLabel);
|
|
249
|
+
jobsSpecs.push({
|
|
250
|
+
job: attachHandle(job, fn, jobLabel),
|
|
251
|
+
options,
|
|
252
|
+
name: taskName
|
|
253
|
+
});
|
|
254
|
+
} catch (error) {
|
|
255
|
+
strapi.log.error(`Could not schedule cron job "${jobLabel}": invalid schedule`, error);
|
|
52
256
|
}
|
|
53
257
|
}
|
|
54
258
|
return this;
|
|
55
259
|
},
|
|
56
260
|
remove (name) {
|
|
57
261
|
if (!name) throw new Error('You must provide a name to remove a cron job.');
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
262
|
+
const remaining = [];
|
|
263
|
+
for (const jobSpec of jobsSpecs){
|
|
264
|
+
if (jobSpec.name === name) {
|
|
265
|
+
jobSpec.job.stop();
|
|
266
|
+
} else {
|
|
267
|
+
remaining.push(jobSpec);
|
|
62
268
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
matchingJobsSpecs.forEach(({ job })=>job.cancel());
|
|
269
|
+
}
|
|
270
|
+
jobsSpecs = remaining;
|
|
66
271
|
return this;
|
|
67
272
|
},
|
|
68
273
|
start () {
|
|
69
|
-
jobsSpecs.forEach(({ job
|
|
274
|
+
jobsSpecs.forEach(({ job })=>job.resume());
|
|
70
275
|
running = true;
|
|
71
276
|
return this;
|
|
72
277
|
},
|
|
73
278
|
stop () {
|
|
74
|
-
jobsSpecs.forEach(({ job })=>job.
|
|
279
|
+
jobsSpecs.forEach(({ job })=>job.pause());
|
|
75
280
|
running = false;
|
|
76
281
|
return this;
|
|
77
282
|
},
|
|
78
283
|
destroy () {
|
|
79
284
|
this.stop();
|
|
285
|
+
jobsSpecs.forEach(({ job })=>job.stop());
|
|
80
286
|
jobsSpecs = [];
|
|
81
287
|
return this;
|
|
82
288
|
},
|
|
83
|
-
jobs
|
|
289
|
+
get jobs () {
|
|
290
|
+
return jobsSpecs;
|
|
291
|
+
}
|
|
84
292
|
};
|
|
85
293
|
};
|
|
86
294
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron.js","sources":["../../src/services/cron.ts"],"sourcesContent":["import type { Job, Spec } from 'node-schedule';\nimport { isFunction } from 'lodash/fp';\nimport type { Core } from '@strapi/types';\n\n// Lazy: only required when a cron task is actually scheduled\nlet lazyNs: typeof import('node-schedule') | undefined;\nconst ns = (): typeof import('node-schedule') => {\n if (!lazyNs) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n lazyNs = require('node-schedule');\n }\n return lazyNs as typeof import('node-schedule');\n};\n\ninterface JobSpec {\n job: Job;\n options: Spec;\n name: string | null;\n}\n\ntype TaskFn = ({ strapi }: { strapi: Core.Strapi }, ...args: unknown[]) => Promise<unknown>;\n\ntype Task =\n | TaskFn\n | {\n task: TaskFn;\n options: Spec;\n };\n\ninterface Tasks {\n [key: string]: Task;\n}\n\nconst createCronService = () => {\n let jobsSpecs: JobSpec[] = [];\n let running = false;\n\n return {\n add(tasks: Tasks = {}) {\n for (const taskExpression of Object.keys(tasks)) {\n const taskValue = tasks[taskExpression];\n\n let fn: TaskFn;\n let options: Spec;\n let taskName: string | null;\n if (isFunction(taskValue)) {\n // don't use task name if key is the rule\n taskName = null;\n fn = taskValue.bind(tasks);\n options = taskExpression;\n } else if (isFunction(taskValue.task)) {\n // set task name if key is not the rule\n taskName = taskExpression;\n fn = taskValue.task.bind(taskValue);\n options = taskValue.options;\n } else {\n throw new Error(\n `Could not schedule a cron job for \"${taskExpression}\": no function found.`\n );\n }\n\n const fnWithStrapi = (...args: unknown[]) => fn({ strapi }, ...args);\n\n // const job = new Job(null, fnWithStrapi);\n const job: Job = new (ns().Job)(fnWithStrapi);\n job.on('error', (error) => {\n strapi.log.error(`Cron job \"${taskName ?? taskExpression}\" failed`, error);\n });\n jobsSpecs.push({ job, options, name: taskName });\n\n if (running) {\n job.schedule(options);\n }\n }\n return this;\n },\n\n remove(name: string) {\n if (!name) throw new Error('You must provide a name to remove a cron job.');\n const matchingJobsSpecs = jobsSpecs.filter(({ name: jobSpecName }, index) => {\n if (jobSpecName === name) {\n jobsSpecs.splice(index, 1);\n return true;\n }\n return false;\n });\n matchingJobsSpecs.forEach(({ job }) => job.cancel());\n return this;\n },\n\n start() {\n jobsSpecs.forEach(({ job, options }) => job.schedule(options));\n running = true;\n return this;\n },\n\n stop() {\n jobsSpecs.forEach(({ job }) => job.cancel());\n running = false;\n return this;\n },\n\n destroy() {\n this.stop();\n jobsSpecs = [];\n return this;\n },\n jobs: jobsSpecs,\n };\n};\n\nexport default createCronService;\n"],"names":["lazyNs","ns","require","createCronService","jobsSpecs","running","add","tasks","taskExpression","Object","keys","taskValue","fn","options","taskName","isFunction","bind","task","Error","fnWithStrapi","args","strapi","job","Job","on","error","log","push","name","schedule","remove","matchingJobsSpecs","filter","jobSpecName","index","splice","forEach","cancel","start","stop","destroy","jobs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cron.js","sources":["../../src/services/cron.ts"],"sourcesContent":["import { isFunction } from 'lodash/fp';\nimport type { Modules } from '@strapi/types';\nimport type { Cron } from 'croner';\n\ntype RecurrenceSpecObjLit = Modules.Cron.RecurrenceSpecObjLit;\ntype CronRuleOptions = Modules.Cron.CronRuleOptions;\ntype CronSchedule = Modules.Cron.CronSchedule;\ntype CronJob = Modules.Cron.CronJob;\ntype JobSpec = Modules.Cron.JobSpec;\ntype TaskFn = Modules.Cron.CronTaskFn;\ntype Tasks = Modules.Cron.CronTasks;\n\nlet lazyCroner: typeof import('croner') | undefined;\nconst getCroner = (): typeof import('croner') => {\n if (!lazyCroner) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n lazyCroner = require('croner');\n }\n return lazyCroner as typeof import('croner');\n};\n\nconst RECURRENCE_KEYS = [\n 'date',\n 'dayOfWeek',\n 'hour',\n 'minute',\n 'month',\n 'second',\n 'year',\n 'recurs',\n] as const;\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !(value instanceof Date);\n\nconst isRuleOptions = (value: unknown): value is CronRuleOptions =>\n isPlainObject(value) && 'rule' in value;\n\nconst isRecurrenceSpec = (value: unknown): value is RecurrenceSpecObjLit =>\n isPlainObject(value) && !('rule' in value) && RECURRENCE_KEYS.some((key) => key in value);\n\nconst toDate = (value: Date | number | string): Date =>\n value instanceof Date ? value : new Date(value);\n\nconst toSegmentPart = (value: unknown, offset = 0): string | null => {\n if (typeof value === 'number') {\n return String(value + offset);\n }\n return typeof value === 'string' ? value : null;\n};\n\nconst segmentToField = (segment: unknown, fallback = '*', offset = 0): string => {\n if (segment == null) {\n return fallback;\n }\n\n const part = toSegmentPart(segment, offset);\n if (part !== null) {\n return part;\n }\n\n if (Array.isArray(segment)) {\n return segment.map((item) => segmentToField(item, fallback, offset)).join(',');\n }\n\n if (isPlainObject(segment)) {\n const start = toSegmentPart(segment.start ?? segment.from, offset);\n const end = toSegmentPart(segment.end ?? segment.to, offset);\n if (start !== null && end !== null) {\n const range = `${start}-${end}`;\n const step = toSegmentPart(segment.step);\n return step === null ? range : `${range}/${step}`;\n }\n }\n\n return fallback;\n};\n\nconst recurrenceToCron = (spec: RecurrenceSpecObjLit): string => {\n const second = segmentToField(spec.second, '0');\n const minute = segmentToField(spec.minute);\n const hour = segmentToField(spec.hour);\n const date = segmentToField(spec.date);\n const month = segmentToField(spec.month, '*', 1);\n const dayOfWeek = segmentToField(spec.dayOfWeek);\n const fields = `${second} ${minute} ${hour} ${date} ${month} ${dayOfWeek}`;\n\n return spec.year == null ? fields : `${fields} ${segmentToField(spec.year)}`;\n};\n\ntype ScheduleWindow = {\n tz?: string;\n start?: Date | number | string;\n end?: Date | number | string;\n};\n\nconst toCronerOptions = ({ tz, start, end }: ScheduleWindow): Record<string, unknown> => {\n const cronerOptions: Record<string, unknown> = {};\n\n if (tz) {\n cronerOptions.timezone = tz;\n }\n if (start != null) {\n cronerOptions.startAt = toDate(start);\n }\n if (end != null) {\n cronerOptions.stopAt = toDate(end);\n }\n\n return cronerOptions;\n};\n\nconst ruleToPattern = (\n rule: CronRuleOptions['rule']\n): { pattern: string | Date; oneShot: boolean } => {\n if (typeof rule === 'number') {\n return { pattern: new Date(rule), oneShot: true };\n }\n if (rule instanceof Date) {\n return { pattern: rule, oneShot: true };\n }\n if (typeof rule === 'string') {\n return { pattern: rule, oneShot: false };\n }\n if (isRecurrenceSpec(rule)) {\n return { pattern: recurrenceToCron(rule), oneShot: false };\n }\n throw new Error('Unsupported cron rule');\n};\n\nconst toCronerArgs = (\n options: CronSchedule\n): { pattern: string | Date; cronerOptions: Record<string, unknown> } => {\n if (typeof options === 'number') {\n return { pattern: new Date(options), cronerOptions: { maxRuns: 1 } };\n }\n\n if (options instanceof Date) {\n return { pattern: options, cronerOptions: { maxRuns: 1 } };\n }\n\n if (typeof options === 'string') {\n return { pattern: options, cronerOptions: {} };\n }\n\n if (isRecurrenceSpec(options)) {\n return {\n pattern: recurrenceToCron(options),\n cronerOptions: toCronerOptions(options),\n };\n }\n\n if (isRuleOptions(options)) {\n const cronerOptions = toCronerOptions(options);\n const { pattern, oneShot } = ruleToPattern(options.rule);\n if (oneShot) {\n cronerOptions.maxRuns = 1;\n }\n\n return { pattern, cronerOptions };\n }\n\n throw new Error('Unsupported cron schedule');\n};\n\nconst createCronService = () => {\n let jobsSpecs: JobSpec[] = [];\n let running = false;\n\n const createRunner = (fn: TaskFn) => {\n let nextFireDate: Date | null = null;\n\n return {\n async run(self?: Cron): Promise<void> {\n const fireDate = nextFireDate ?? self?.currentRun() ?? new Date();\n nextFireDate = self?.nextRun(fireDate) ?? null;\n await fn({ strapi }, fireDate);\n },\n setNextRun(date: Date | null) {\n nextFireDate = date;\n },\n };\n };\n\n const createCronJob = (\n pattern: string | Date,\n cronerOptions: Record<string, unknown>,\n fn: TaskFn,\n jobLabel: string\n ) => {\n const { Cron: CronCtor } = getCroner();\n const runner = createRunner(fn);\n const job = new CronCtor(\n pattern,\n {\n paused: !running,\n // cron-parser (via node-schedule) accepted numeric-prefix stepping\n // like `5/15` as `5-59/15`. Croner 10 rejects it unless this is set.\n sloppyRanges: true,\n ...cronerOptions,\n catch(error: unknown) {\n strapi.log.error(`Cron job \"${jobLabel}\" failed`, error);\n },\n },\n runner.run\n );\n runner.setNextRun(job.nextRun());\n return job;\n };\n\n const attachHandle = (job: Cron, fn: TaskFn, jobLabel: string): CronJob => {\n let current = job;\n let handle: CronJob;\n\n const invoke = async () => {\n const fireDate = current.currentRun() ?? new Date();\n return fn({ strapi }, fireDate);\n };\n\n const cancel = () => {\n current.stop();\n return true;\n };\n\n const nextInvocation = () => current.nextRun();\n\n const reschedule = (spec: CronSchedule) => {\n try {\n const idx = jobsSpecs.findIndex((jobSpec) => jobSpec.job === handle);\n if (idx === -1) {\n return false;\n }\n\n const { pattern, cronerOptions } = toCronerArgs(spec);\n const next = createCronJob(pattern, cronerOptions, fn, jobLabel);\n\n current.stop();\n current = next;\n jobsSpecs[idx] = {\n ...jobsSpecs[idx],\n options: spec,\n };\n return true;\n } catch (error) {\n strapi.log.error(`Could not reschedule cron job \"${jobLabel}\"`, error);\n return false;\n }\n };\n\n handle = new Proxy(job as CronJob, {\n get(_target, property) {\n if (property === 'invoke') return invoke;\n if (property === 'cancel') return cancel;\n if (property === 'nextInvocation') return nextInvocation;\n if (property === 'reschedule') return reschedule;\n\n const value = Reflect.get(current, property, current);\n return typeof value === 'function' ? value.bind(current) : value;\n },\n });\n\n return handle;\n };\n\n return {\n add(tasks: Tasks = {}) {\n for (const taskExpression of Object.keys(tasks)) {\n const taskValue = tasks[taskExpression];\n\n let fn: TaskFn;\n let options: CronSchedule;\n let taskName: string | null;\n if (isFunction(taskValue)) {\n taskName = null;\n fn = taskValue.bind(tasks);\n options = taskExpression;\n } else if (isFunction(taskValue.task)) {\n taskName = taskExpression;\n fn = taskValue.task.bind(taskValue);\n options = taskValue.options;\n } else {\n throw new Error(\n `Could not schedule a cron job for \"${taskExpression}\": no function found.`\n );\n }\n\n const jobLabel = taskName ?? taskExpression;\n\n try {\n const { pattern, cronerOptions } = toCronerArgs(options);\n const job = createCronJob(pattern, cronerOptions, fn, jobLabel);\n\n jobsSpecs.push({\n job: attachHandle(job, fn, jobLabel),\n options,\n name: taskName,\n });\n } catch (error) {\n strapi.log.error(`Could not schedule cron job \"${jobLabel}\": invalid schedule`, error);\n }\n }\n return this;\n },\n\n remove(name: string) {\n if (!name) throw new Error('You must provide a name to remove a cron job.');\n const remaining: JobSpec[] = [];\n for (const jobSpec of jobsSpecs) {\n if (jobSpec.name === name) {\n jobSpec.job.stop();\n } else {\n remaining.push(jobSpec);\n }\n }\n jobsSpecs = remaining;\n return this;\n },\n\n start() {\n jobsSpecs.forEach(({ job }) => job.resume());\n running = true;\n return this;\n },\n\n stop() {\n jobsSpecs.forEach(({ job }) => job.pause());\n running = false;\n return this;\n },\n\n destroy() {\n this.stop();\n jobsSpecs.forEach(({ job }) => job.stop());\n jobsSpecs = [];\n return this;\n },\n\n get jobs() {\n return jobsSpecs;\n },\n };\n};\n\nexport default createCronService;\n"],"names":["lazyCroner","getCroner","require","RECURRENCE_KEYS","isPlainObject","value","Date","isRuleOptions","isRecurrenceSpec","some","key","toDate","toSegmentPart","offset","String","segmentToField","segment","fallback","part","Array","isArray","map","item","join","start","from","end","to","range","step","recurrenceToCron","spec","second","minute","hour","date","month","dayOfWeek","fields","year","toCronerOptions","tz","cronerOptions","timezone","startAt","stopAt","ruleToPattern","rule","pattern","oneShot","Error","toCronerArgs","options","maxRuns","createCronService","jobsSpecs","running","createRunner","fn","nextFireDate","run","self","fireDate","currentRun","nextRun","strapi","setNextRun","createCronJob","jobLabel","Cron","CronCtor","runner","job","paused","sloppyRanges","catch","error","log","attachHandle","current","handle","invoke","cancel","stop","nextInvocation","reschedule","idx","findIndex","jobSpec","next","Proxy","get","_target","property","Reflect","bind","add","tasks","taskExpression","Object","keys","taskValue","taskName","isFunction","task","push","name","remove","remaining","forEach","resume","pause","destroy","jobs"],"mappings":";;;;AAYA,IAAIA,UAAAA;AACJ,MAAMC,SAAAA,GAAY,IAAA;AAChB,IAAA,IAAI,CAACD,UAAAA,EAAY;;AAEfA,QAAAA,UAAAA,GAAaE,OAAAA,CAAQ,QAAA,CAAA;AACvB,IAAA;IACA,OAAOF,UAAAA;AACT,CAAA;AAEA,MAAMG,eAAAA,GAAkB;AACtB,IAAA,MAAA;AACA,IAAA,WAAA;AACA,IAAA,MAAA;AACA,IAAA,QAAA;AACA,IAAA,OAAA;AACA,IAAA,QAAA;AACA,IAAA,MAAA;AACA,IAAA;AACD,CAAA;AAED,MAAMC,aAAAA,GAAgB,CAACC,KAAAA,GACrB,OAAOA,KAAAA,KAAU,QAAA,IAAYA,KAAAA,KAAU,IAAA,IAAQ,EAAEA,KAAAA,YAAiBC,IAAG,CAAA;AAEvE,MAAMC,aAAAA,GAAgB,CAACF,KAAAA,GACrBD,aAAAA,CAAcC,UAAU,MAAA,IAAUA,KAAAA;AAEpC,MAAMG,gBAAAA,GAAmB,CAACH,KAAAA,GACxBD,aAAAA,CAAcC,UAAU,EAAE,MAAA,IAAUA,KAAI,KAAMF,eAAAA,CAAgBM,IAAI,CAAC,CAACC,MAAQA,GAAAA,IAAOL,KAAAA,CAAAA;AAErF,MAAMM,SAAS,CAACN,KAAAA,GACdA,iBAAiBC,IAAAA,GAAOD,KAAAA,GAAQ,IAAIC,IAAAA,CAAKD,KAAAA,CAAAA;AAE3C,MAAMO,aAAAA,GAAgB,CAACP,KAAAA,EAAgBQ,MAAAA,GAAS,CAAC,GAAA;IAC/C,IAAI,OAAOR,UAAU,QAAA,EAAU;AAC7B,QAAA,OAAOS,OAAOT,KAAAA,GAAQQ,MAAAA,CAAAA;AACxB,IAAA;IACA,OAAO,OAAOR,KAAAA,KAAU,QAAA,GAAWA,KAAAA,GAAQ,IAAA;AAC7C,CAAA;AAEA,MAAMU,iBAAiB,CAACC,OAAAA,EAAkBC,WAAW,GAAG,EAAEJ,SAAS,CAAC,GAAA;AAClE,IAAA,IAAIG,WAAW,IAAA,EAAM;QACnB,OAAOC,QAAAA;AACT,IAAA;IAEA,MAAMC,IAAAA,GAAON,cAAcI,OAAAA,EAASH,MAAAA,CAAAA;AACpC,IAAA,IAAIK,SAAS,IAAA,EAAM;QACjB,OAAOA,IAAAA;AACT,IAAA;IAEA,IAAIC,KAAAA,CAAMC,OAAO,CAACJ,OAAAA,CAAAA,EAAU;QAC1B,OAAOA,OAAAA,CAAQK,GAAG,CAAC,CAACC,IAAAA,GAASP,eAAeO,IAAAA,EAAML,QAAAA,EAAUJ,MAAAA,CAAAA,CAAAA,CAASU,IAAI,CAAC,GAAA,CAAA;AAC5E,IAAA;AAEA,IAAA,IAAInB,cAAcY,OAAAA,CAAAA,EAAU;AAC1B,QAAA,MAAMQ,QAAQZ,aAAAA,CAAcI,OAAAA,CAAQQ,KAAK,IAAIR,OAAAA,CAAQS,IAAI,EAAEZ,MAAAA,CAAAA;AAC3D,QAAA,MAAMa,MAAMd,aAAAA,CAAcI,OAAAA,CAAQU,GAAG,IAAIV,OAAAA,CAAQW,EAAE,EAAEd,MAAAA,CAAAA;QACrD,IAAIW,KAAAA,KAAU,IAAA,IAAQE,GAAAA,KAAQ,IAAA,EAAM;AAClC,YAAA,MAAME,KAAAA,GAAQ,CAAA,EAAGJ,KAAAA,CAAM,CAAC,EAAEE,GAAAA,CAAAA,CAAK;YAC/B,MAAMG,IAAAA,GAAOjB,aAAAA,CAAcI,OAAAA,CAAQa,IAAI,CAAA;AACvC,YAAA,OAAOA,SAAS,IAAA,GAAOD,KAAAA,GAAQ,GAAGA,KAAAA,CAAM,CAAC,EAAEC,IAAAA,CAAAA,CAAM;AACnD,QAAA;AACF,IAAA;IAEA,OAAOZ,QAAAA;AACT,CAAA;AAEA,MAAMa,mBAAmB,CAACC,IAAAA,GAAAA;AACxB,IAAA,MAAMC,MAAAA,GAASjB,cAAAA,CAAegB,IAAAA,CAAKC,MAAM,EAAE,GAAA,CAAA;IAC3C,MAAMC,MAAAA,GAASlB,cAAAA,CAAegB,IAAAA,CAAKE,MAAM,CAAA;IACzC,MAAMC,IAAAA,GAAOnB,cAAAA,CAAegB,IAAAA,CAAKG,IAAI,CAAA;IACrC,MAAMC,IAAAA,GAAOpB,cAAAA,CAAegB,IAAAA,CAAKI,IAAI,CAAA;AACrC,IAAA,MAAMC,KAAAA,GAAQrB,cAAAA,CAAegB,IAAAA,CAAKK,KAAK,EAAE,GAAA,EAAK,CAAA,CAAA;IAC9C,MAAMC,SAAAA,GAAYtB,cAAAA,CAAegB,IAAAA,CAAKM,SAAS,CAAA;AAC/C,IAAA,MAAMC,SAAS,CAAA,EAAGN,MAAAA,CAAO,CAAC,EAAEC,OAAO,CAAC,EAAEC,IAAAA,CAAK,CAAC,EAAEC,IAAAA,CAAK,CAAC,EAAEC,KAAAA,CAAM,CAAC,EAAEC,SAAAA,CAAAA,CAAW;AAE1E,IAAA,OAAON,IAAAA,CAAKQ,IAAI,IAAI,IAAA,GAAOD,MAAAA,GAAS,CAAA,EAAGA,MAAAA,CAAO,CAAC,EAAEvB,cAAAA,CAAegB,IAAAA,CAAKQ,IAAI,CAAA,CAAA,CAAG;AAC9E,CAAA;AAQA,MAAMC,eAAAA,GAAkB,CAAC,EAAEC,EAAE,EAAEjB,KAAK,EAAEE,GAAG,EAAkB,GAAA;AACzD,IAAA,MAAMgB,gBAAyC,EAAC;AAEhD,IAAA,IAAID,EAAAA,EAAI;AACNC,QAAAA,aAAAA,CAAcC,QAAQ,GAAGF,EAAAA;AAC3B,IAAA;AACA,IAAA,IAAIjB,SAAS,IAAA,EAAM;QACjBkB,aAAAA,CAAcE,OAAO,GAAGjC,MAAAA,CAAOa,KAAAA,CAAAA;AACjC,IAAA;AACA,IAAA,IAAIE,OAAO,IAAA,EAAM;QACfgB,aAAAA,CAAcG,MAAM,GAAGlC,MAAAA,CAAOe,GAAAA,CAAAA;AAChC,IAAA;IAEA,OAAOgB,aAAAA;AACT,CAAA;AAEA,MAAMI,gBAAgB,CACpBC,IAAAA,GAAAA;IAEA,IAAI,OAAOA,SAAS,QAAA,EAAU;QAC5B,OAAO;AAAEC,YAAAA,OAAAA,EAAS,IAAI1C,IAAAA,CAAKyC,IAAAA,CAAAA;YAAOE,OAAAA,EAAS;AAAK,SAAA;AAClD,IAAA;AACA,IAAA,IAAIF,gBAAgBzC,IAAAA,EAAM;QACxB,OAAO;YAAE0C,OAAAA,EAASD,IAAAA;YAAME,OAAAA,EAAS;AAAK,SAAA;AACxC,IAAA;IACA,IAAI,OAAOF,SAAS,QAAA,EAAU;QAC5B,OAAO;YAAEC,OAAAA,EAASD,IAAAA;YAAME,OAAAA,EAAS;AAAM,SAAA;AACzC,IAAA;AACA,IAAA,IAAIzC,iBAAiBuC,IAAAA,CAAAA,EAAO;QAC1B,OAAO;AAAEC,YAAAA,OAAAA,EAASlB,gBAAAA,CAAiBiB,IAAAA,CAAAA;YAAOE,OAAAA,EAAS;AAAM,SAAA;AAC3D,IAAA;AACA,IAAA,MAAM,IAAIC,KAAAA,CAAM,uBAAA,CAAA;AAClB,CAAA;AAEA,MAAMC,eAAe,CACnBC,OAAAA,GAAAA;IAEA,IAAI,OAAOA,YAAY,QAAA,EAAU;QAC/B,OAAO;AAAEJ,YAAAA,OAAAA,EAAS,IAAI1C,IAAAA,CAAK8C,OAAAA,CAAAA;YAAUV,aAAAA,EAAe;gBAAEW,OAAAA,EAAS;AAAE;AAAE,SAAA;AACrE,IAAA;AAEA,IAAA,IAAID,mBAAmB9C,IAAAA,EAAM;QAC3B,OAAO;YAAE0C,OAAAA,EAASI,OAAAA;YAASV,aAAAA,EAAe;gBAAEW,OAAAA,EAAS;AAAE;AAAE,SAAA;AAC3D,IAAA;IAEA,IAAI,OAAOD,YAAY,QAAA,EAAU;QAC/B,OAAO;YAAEJ,OAAAA,EAASI,OAAAA;AAASV,YAAAA,aAAAA,EAAe;AAAG,SAAA;AAC/C,IAAA;AAEA,IAAA,IAAIlC,iBAAiB4C,OAAAA,CAAAA,EAAU;QAC7B,OAAO;AACLJ,YAAAA,OAAAA,EAASlB,gBAAAA,CAAiBsB,OAAAA,CAAAA;AAC1BV,YAAAA,aAAAA,EAAeF,eAAAA,CAAgBY,OAAAA;AACjC,SAAA;AACF,IAAA;AAEA,IAAA,IAAI7C,cAAc6C,OAAAA,CAAAA,EAAU;AAC1B,QAAA,MAAMV,gBAAgBF,eAAAA,CAAgBY,OAAAA,CAAAA;QACtC,MAAM,EAAEJ,OAAO,EAAEC,OAAO,EAAE,GAAGH,aAAAA,CAAcM,QAAQL,IAAI,CAAA;AACvD,QAAA,IAAIE,OAAAA,EAAS;AACXP,YAAAA,aAAAA,CAAcW,OAAO,GAAG,CAAA;AAC1B,QAAA;QAEA,OAAO;AAAEL,YAAAA,OAAAA;AAASN,YAAAA;AAAc,SAAA;AAClC,IAAA;AAEA,IAAA,MAAM,IAAIQ,KAAAA,CAAM,2BAAA,CAAA;AAClB,CAAA;AAEA,MAAMI,iBAAAA,GAAoB,IAAA;AACxB,IAAA,IAAIC,YAAuB,EAAE;AAC7B,IAAA,IAAIC,OAAAA,GAAU,KAAA;AAEd,IAAA,MAAMC,eAAe,CAACC,EAAAA,GAAAA;AACpB,QAAA,IAAIC,YAAAA,GAA4B,IAAA;QAEhC,OAAO;AACL,YAAA,MAAMC,KAAIC,IAAW,EAAA;AACnB,gBAAA,MAAMC,QAAAA,GAAWH,YAAAA,IAAgBE,IAAAA,EAAME,UAAAA,EAAAA,IAAgB,IAAIzD,IAAAA,EAAAA;gBAC3DqD,YAAAA,GAAeE,IAAAA,EAAMG,QAAQF,QAAAA,CAAAA,IAAa,IAAA;AAC1C,gBAAA,MAAMJ,EAAAA,CAAG;AAAEO,oBAAAA;iBAAO,EAAGH,QAAAA,CAAAA;AACvB,YAAA,CAAA;AACAI,YAAAA,UAAAA,CAAAA,CAAW/B,IAAiB,EAAA;gBAC1BwB,YAAAA,GAAexB,IAAAA;AACjB,YAAA;AACF,SAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMgC,aAAAA,GAAgB,CACpBnB,OAAAA,EACAN,aAAAA,EACAgB,EAAAA,EACAU,QAAAA,GAAAA;AAEA,QAAA,MAAM,EAAEC,IAAAA,EAAMC,QAAQ,EAAE,GAAGrE,SAAAA,EAAAA;AAC3B,QAAA,MAAMsE,SAASd,YAAAA,CAAaC,EAAAA,CAAAA;QAC5B,MAAMc,GAAAA,GAAM,IAAIF,QAAAA,CACdtB,OAAAA,EACA;AACEyB,YAAAA,MAAAA,EAAQ,CAACjB,OAAAA;;;YAGTkB,YAAAA,EAAc,IAAA;AACd,YAAA,GAAGhC,aAAa;AAChBiC,YAAAA,KAAAA,CAAAA,CAAMC,KAAc,EAAA;gBAClBX,MAAAA,CAAOY,GAAG,CAACD,KAAK,CAAC,CAAC,UAAU,EAAER,QAAAA,CAAS,QAAQ,CAAC,EAAEQ,KAAAA,CAAAA;AACpD,YAAA;AACF,SAAA,EACAL,OAAOX,GAAG,CAAA;QAEZW,MAAAA,CAAOL,UAAU,CAACM,GAAAA,CAAIR,OAAO,EAAA,CAAA;QAC7B,OAAOQ,GAAAA;AACT,IAAA,CAAA;IAEA,MAAMM,YAAAA,GAAe,CAACN,GAAAA,EAAWd,EAAAA,EAAYU,QAAAA,GAAAA;AAC3C,QAAA,IAAIW,OAAAA,GAAUP,GAAAA;QACd,IAAIQ,MAAAA;AAEJ,QAAA,MAAMC,MAAAA,GAAS,UAAA;AACb,YAAA,MAAMnB,QAAAA,GAAWiB,OAAAA,CAAQhB,UAAU,EAAA,IAAM,IAAIzD,IAAAA,EAAAA;AAC7C,YAAA,OAAOoD,EAAAA,CAAG;AAAEO,gBAAAA;aAAO,EAAGH,QAAAA,CAAAA;AACxB,QAAA,CAAA;AAEA,QAAA,MAAMoB,MAAAA,GAAS,IAAA;AACbH,YAAAA,OAAAA,CAAQI,IAAI,EAAA;YACZ,OAAO,IAAA;AACT,QAAA,CAAA;QAEA,MAAMC,cAAAA,GAAiB,IAAML,OAAAA,CAAQf,OAAO,EAAA;AAE5C,QAAA,MAAMqB,aAAa,CAACtD,IAAAA,GAAAA;YAClB,IAAI;gBACF,MAAMuD,GAAAA,GAAM/B,UAAUgC,SAAS,CAAC,CAACC,OAAAA,GAAYA,OAAAA,CAAQhB,GAAG,KAAKQ,MAAAA,CAAAA;gBAC7D,IAAIM,GAAAA,KAAQ,CAAC,CAAA,EAAG;oBACd,OAAO,KAAA;AACT,gBAAA;AAEA,gBAAA,MAAM,EAAEtC,OAAO,EAAEN,aAAa,EAAE,GAAGS,YAAAA,CAAapB,IAAAA,CAAAA;AAChD,gBAAA,MAAM0D,IAAAA,GAAOtB,aAAAA,CAAcnB,OAAAA,EAASN,aAAAA,EAAegB,EAAAA,EAAIU,QAAAA,CAAAA;AAEvDW,gBAAAA,OAAAA,CAAQI,IAAI,EAAA;gBACZJ,OAAAA,GAAUU,IAAAA;gBACVlC,SAAS,CAAC+B,IAAI,GAAG;oBACf,GAAG/B,SAAS,CAAC+B,GAAAA,CAAI;oBACjBlC,OAAAA,EAASrB;AACX,iBAAA;gBACA,OAAO,IAAA;AACT,YAAA,CAAA,CAAE,OAAO6C,KAAAA,EAAO;gBACdX,MAAAA,CAAOY,GAAG,CAACD,KAAK,CAAC,CAAC,+BAA+B,EAAER,QAAAA,CAAS,CAAC,CAAC,EAAEQ,KAAAA,CAAAA;gBAChE,OAAO,KAAA;AACT,YAAA;AACF,QAAA,CAAA;QAEAI,MAAAA,GAAS,IAAIU,MAAMlB,GAAAA,EAAgB;YACjCmB,GAAAA,CAAAA,CAAIC,OAAO,EAAEC,QAAQ,EAAA;gBACnB,IAAIA,QAAAA,KAAa,UAAU,OAAOZ,MAAAA;gBAClC,IAAIY,QAAAA,KAAa,UAAU,OAAOX,MAAAA;gBAClC,IAAIW,QAAAA,KAAa,kBAAkB,OAAOT,cAAAA;gBAC1C,IAAIS,QAAAA,KAAa,cAAc,OAAOR,UAAAA;AAEtC,gBAAA,MAAMhF,KAAAA,GAAQyF,OAAAA,CAAQH,GAAG,CAACZ,SAASc,QAAAA,EAAUd,OAAAA,CAAAA;AAC7C,gBAAA,OAAO,OAAO1E,KAAAA,KAAU,UAAA,GAAaA,KAAAA,CAAM0F,IAAI,CAAChB,OAAAA,CAAAA,GAAW1E,KAAAA;AAC7D,YAAA;AACF,SAAA,CAAA;QAEA,OAAO2E,MAAAA;AACT,IAAA,CAAA;IAEA,OAAO;QACLgB,GAAAA,CAAAA,CAAIC,KAAAA,GAAe,EAAE,EAAA;AACnB,YAAA,KAAK,MAAMC,cAAAA,IAAkBC,MAAAA,CAAOC,IAAI,CAACH,KAAAA,CAAAA,CAAQ;gBAC/C,MAAMI,SAAAA,GAAYJ,KAAK,CAACC,cAAAA,CAAe;gBAEvC,IAAIxC,EAAAA;gBACJ,IAAIN,OAAAA;gBACJ,IAAIkD,QAAAA;AACJ,gBAAA,IAAIC,cAAWF,SAAAA,CAAAA,EAAY;oBACzBC,QAAAA,GAAW,IAAA;oBACX5C,EAAAA,GAAK2C,SAAAA,CAAUN,IAAI,CAACE,KAAAA,CAAAA;oBACpB7C,OAAAA,GAAU8C,cAAAA;AACZ,gBAAA,CAAA,MAAO,IAAIK,aAAAA,CAAWF,SAAAA,CAAUG,IAAI,CAAA,EAAG;oBACrCF,QAAAA,GAAWJ,cAAAA;AACXxC,oBAAAA,EAAAA,GAAK2C,SAAAA,CAAUG,IAAI,CAACT,IAAI,CAACM,SAAAA,CAAAA;AACzBjD,oBAAAA,OAAAA,GAAUiD,UAAUjD,OAAO;gBAC7B,CAAA,MAAO;AACL,oBAAA,MAAM,IAAIF,KAAAA,CACR,CAAC,mCAAmC,EAAEgD,cAAAA,CAAe,qBAAqB,CAAC,CAAA;AAE/E,gBAAA;AAEA,gBAAA,MAAM9B,WAAWkC,QAAAA,IAAYJ,cAAAA;gBAE7B,IAAI;AACF,oBAAA,MAAM,EAAElD,OAAO,EAAEN,aAAa,EAAE,GAAGS,YAAAA,CAAaC,OAAAA,CAAAA;AAChD,oBAAA,MAAMoB,GAAAA,GAAML,aAAAA,CAAcnB,OAAAA,EAASN,aAAAA,EAAegB,EAAAA,EAAIU,QAAAA,CAAAA;AAEtDb,oBAAAA,SAAAA,CAAUkD,IAAI,CAAC;wBACbjC,GAAAA,EAAKM,YAAAA,CAAaN,KAAKd,EAAAA,EAAIU,QAAAA,CAAAA;AAC3BhB,wBAAAA,OAAAA;wBACAsD,IAAAA,EAAMJ;AACR,qBAAA,CAAA;AACF,gBAAA,CAAA,CAAE,OAAO1B,KAAAA,EAAO;oBACdX,MAAAA,CAAOY,GAAG,CAACD,KAAK,CAAC,CAAC,6BAA6B,EAAER,QAAAA,CAAS,mBAAmB,CAAC,EAAEQ,KAAAA,CAAAA;AAClF,gBAAA;AACF,YAAA;AACA,YAAA,OAAO,IAAI;AACb,QAAA,CAAA;AAEA+B,QAAAA,MAAAA,CAAAA,CAAOD,IAAY,EAAA;AACjB,YAAA,IAAI,CAACA,IAAAA,EAAM,MAAM,IAAIxD,KAAAA,CAAM,+CAAA,CAAA;AAC3B,YAAA,MAAM0D,YAAuB,EAAE;YAC/B,KAAK,MAAMpB,WAAWjC,SAAAA,CAAW;gBAC/B,IAAIiC,OAAAA,CAAQkB,IAAI,KAAKA,IAAAA,EAAM;oBACzBlB,OAAAA,CAAQhB,GAAG,CAACW,IAAI,EAAA;gBAClB,CAAA,MAAO;AACLyB,oBAAAA,SAAAA,CAAUH,IAAI,CAACjB,OAAAA,CAAAA;AACjB,gBAAA;AACF,YAAA;YACAjC,SAAAA,GAAYqD,SAAAA;AACZ,YAAA,OAAO,IAAI;AACb,QAAA,CAAA;AAEApF,QAAAA,KAAAA,CAAAA,GAAAA;YACE+B,SAAAA,CAAUsD,OAAO,CAAC,CAAC,EAAErC,GAAG,EAAE,GAAKA,IAAIsC,MAAM,EAAA,CAAA;YACzCtD,OAAAA,GAAU,IAAA;AACV,YAAA,OAAO,IAAI;AACb,QAAA,CAAA;AAEA2B,QAAAA,IAAAA,CAAAA,GAAAA;YACE5B,SAAAA,CAAUsD,OAAO,CAAC,CAAC,EAAErC,GAAG,EAAE,GAAKA,IAAIuC,KAAK,EAAA,CAAA;YACxCvD,OAAAA,GAAU,KAAA;AACV,YAAA,OAAO,IAAI;AACb,QAAA,CAAA;AAEAwD,QAAAA,OAAAA,CAAAA,GAAAA;AACE,YAAA,IAAI,CAAC7B,IAAI,EAAA;YACT5B,SAAAA,CAAUsD,OAAO,CAAC,CAAC,EAAErC,GAAG,EAAE,GAAKA,IAAIW,IAAI,EAAA,CAAA;AACvC5B,YAAAA,SAAAA,GAAY,EAAE;AACd,YAAA,OAAO,IAAI;AACb,QAAA,CAAA;AAEA,QAAA,IAAI0D,IAAAA,CAAAA,GAAO;YACT,OAAO1D,SAAAA;AACT,QAAA;AACF,KAAA;AACF;;;;"}
|