@sequoialabs/payload-plugin-reversia 0.1.8 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/use-trigger-crawl.d.ts +30 -0
- package/dist/client/use-trigger-crawl.js +43 -0
- package/dist/endpoints/confirm-resources-sync.d.ts +1 -1
- package/dist/endpoints/confirm-resources-sync.js +2 -2
- package/dist/endpoints/resource.d.ts +1 -1
- package/dist/endpoints/resource.js +3 -3
- package/dist/endpoints/resources-definition.d.ts +1 -1
- package/dist/endpoints/resources-definition.js +3 -3
- package/dist/endpoints/resources-insert.d.ts +1 -1
- package/dist/endpoints/resources-insert.js +26 -5
- package/dist/endpoints/resources-sync.d.ts +1 -1
- package/dist/endpoints/resources-sync.js +3 -3
- package/dist/endpoints/resources.d.ts +1 -1
- package/dist/endpoints/resources.js +4 -4
- package/dist/endpoints/settings.d.ts +1 -1
- package/dist/endpoints/settings.js +1 -1
- package/dist/endpoints/trigger-crawl-dashboard.d.ts +3 -0
- package/dist/endpoints/trigger-crawl-dashboard.js +34 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +14 -11
- package/dist/trigger-crawl.d.ts +24 -0
- package/dist/trigger-crawl.js +47 -0
- package/dist/types.d.ts +7 -1
- package/dist/utils/cursor.d.ts +1 -1
- package/dist/utils/fields.d.ts +2 -2
- package/dist/utils/fields.js +4 -4
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ Then mark whichever fields are translatable. **You don't need to annotate contai
|
|
|
71
71
|
- **[Field annotations](./docs/field-annotations.md)** — the `custom.reversia` metadata reference.
|
|
72
72
|
- **[Rich text & JSON fields](./docs/rich-text.md)** — `translatableKeys`, path patterns, and the `extract`/`apply` escape hatch.
|
|
73
73
|
- **[API reference](./docs/api-reference.md)** — HTTP endpoints consumed by the Reversia SaaS.
|
|
74
|
+
- **[Triggering crawls](./docs/trigger-crawl.md)** — `triggerCrawl()` for server code and `useTriggerCrawl()` for admin UI.
|
|
74
75
|
- **[Contributing](./CONTRIBUTING.md)** — local dev, linting, testing.
|
|
75
76
|
|
|
76
77
|
## Requirements
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type TriggerCrawlArgs, type TriggerCrawlOutcome, type TriggerCrawlStatus, type UseTriggerCrawlOptions, type UseTriggerCrawlResult, useTriggerCrawl, } from './use-trigger-crawl';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useTriggerCrawl, } from './use-trigger-crawl';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CollectionSlug, GlobalSlug } from 'payload';
|
|
2
|
+
export type TriggerCrawlStatus = 'idle' | 'pending' | 'success' | 'error';
|
|
3
|
+
export interface UseTriggerCrawlOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Override the server endpoint path. Defaults to
|
|
6
|
+
* `/api/reversia/dashboard/trigger-crawl`.
|
|
7
|
+
*/
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TriggerCrawlArgs {
|
|
11
|
+
/**
|
|
12
|
+
* Collection and/or global slugs to crawl. Slugs are prefixed with
|
|
13
|
+
* `payloadcms:` server-side before reaching Reversia. Omit/empty to crawl
|
|
14
|
+
* every enabled resource for the project.
|
|
15
|
+
*/
|
|
16
|
+
types?: Array<CollectionSlug | GlobalSlug>;
|
|
17
|
+
/** Bypass cached crawl content. Defaults to `false`. */
|
|
18
|
+
noCache?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface TriggerCrawlOutcome {
|
|
21
|
+
success: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface UseTriggerCrawlResult {
|
|
25
|
+
trigger: (args?: TriggerCrawlArgs) => Promise<TriggerCrawlOutcome>;
|
|
26
|
+
status: TriggerCrawlStatus;
|
|
27
|
+
error: string | null;
|
|
28
|
+
reset: () => void;
|
|
29
|
+
}
|
|
30
|
+
export declare function useTriggerCrawl(options?: UseTriggerCrawlOptions): UseTriggerCrawlResult;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
|
+
const DEFAULT_ENDPOINT = '/api/reversia/dashboard/trigger-crawl';
|
|
4
|
+
export function useTriggerCrawl(options = {}) {
|
|
5
|
+
const endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
|
|
6
|
+
const [status, setStatus] = useState('idle');
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
const trigger = useCallback(async (args = {}) => {
|
|
9
|
+
setStatus('pending');
|
|
10
|
+
setError(null);
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(endpoint, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: { 'Content-Type': 'application/json' },
|
|
15
|
+
credentials: 'same-origin',
|
|
16
|
+
body: JSON.stringify({
|
|
17
|
+
...(args.types && args.types.length > 0 ? { types: args.types } : {}),
|
|
18
|
+
...(args.noCache ? { noCache: true } : {}),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
const body = (await response.json().catch(() => ({})));
|
|
22
|
+
if (!response.ok || body.success === false) {
|
|
23
|
+
const message = body.error ?? `Request failed (${response.status})`;
|
|
24
|
+
setStatus('error');
|
|
25
|
+
setError(message);
|
|
26
|
+
return { success: false, error: message };
|
|
27
|
+
}
|
|
28
|
+
setStatus('success');
|
|
29
|
+
return { success: true };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
33
|
+
setStatus('error');
|
|
34
|
+
setError(message);
|
|
35
|
+
return { success: false, error: message };
|
|
36
|
+
}
|
|
37
|
+
}, [endpoint]);
|
|
38
|
+
const reset = useCallback(() => {
|
|
39
|
+
setStatus('idle');
|
|
40
|
+
setError(null);
|
|
41
|
+
}, []);
|
|
42
|
+
return { trigger, status, error, reset };
|
|
43
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { unauthorizedResponse, validateApiKey } from '../utils/auth
|
|
2
|
-
import { decodeCursor } from '../utils/cursor
|
|
1
|
+
import { unauthorizedResponse, validateApiKey } from '../utils/auth';
|
|
2
|
+
import { decodeCursor } from '../utils/cursor';
|
|
3
3
|
export function createConfirmResourcesSyncEndpoint(pluginConfig) {
|
|
4
4
|
return {
|
|
5
5
|
path: '/reversia/confirm-resources-sync',
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CollectionConfig, Endpoint, GlobalConfig } from 'payload';
|
|
2
|
-
import type { ReversiaPluginConfig } from '../types
|
|
2
|
+
import type { ReversiaPluginConfig } from '../types';
|
|
3
3
|
export declare function createResourceEndpoint(pluginConfig: ReversiaPluginConfig, collectionsMap: Map<string, CollectionConfig>, globalsMap: Map<string, GlobalConfig>): Endpoint;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { unauthorizedResponse, validateApiKey } from '../utils/auth
|
|
2
|
-
import { findLocalizedFields, serializeField } from '../utils/fields
|
|
3
|
-
import { resolveDefaultLocale } from '../utils/payload-helpers
|
|
1
|
+
import { unauthorizedResponse, validateApiKey } from '../utils/auth';
|
|
2
|
+
import { findLocalizedFields, serializeField } from '../utils/fields';
|
|
3
|
+
import { resolveDefaultLocale } from '../utils/payload-helpers';
|
|
4
4
|
function extract(doc, fields) {
|
|
5
5
|
const content = {};
|
|
6
6
|
const contentTypes = {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CollectionConfig, Endpoint, GlobalConfig } from 'payload';
|
|
2
|
-
import type { ReversiaPluginConfig } from '../types
|
|
2
|
+
import type { ReversiaPluginConfig } from '../types';
|
|
3
3
|
export declare function createResourcesDefinitionEndpoint(pluginConfig: ReversiaPluginConfig, collectionsMap: Map<string, CollectionConfig>, globalsMap: Map<string, GlobalConfig>): Endpoint;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { unauthorizedResponse, validateApiKey } from '../utils/auth
|
|
2
|
-
import { buildTranslatableConfiguration, findLocalizedFields } from '../utils/fields
|
|
3
|
-
import { resolveStaticLabel } from '../utils/labels
|
|
1
|
+
import { unauthorizedResponse, validateApiKey } from '../utils/auth';
|
|
2
|
+
import { buildTranslatableConfiguration, findLocalizedFields } from '../utils/fields';
|
|
3
|
+
import { resolveStaticLabel } from '../utils/labels';
|
|
4
4
|
export function createResourcesDefinitionEndpoint(pluginConfig, collectionsMap, globalsMap) {
|
|
5
5
|
return {
|
|
6
6
|
path: '/reversia/resources-definition',
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CollectionConfig, Endpoint, GlobalConfig } from 'payload';
|
|
2
|
-
import type { ReversiaPluginConfig } from '../types
|
|
2
|
+
import type { ReversiaPluginConfig } from '../types';
|
|
3
3
|
export declare function createResourcesInsertEndpoint(pluginConfig: ReversiaPluginConfig, collectionsMap: Map<string, CollectionConfig>, globalsMap: Map<string, GlobalConfig>): Endpoint;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { unauthorizedResponse, validateApiKey } from '../utils/auth
|
|
2
|
-
import { deflatePopulatedRelationships, deserializeFieldValue, findLocalizedFields, } from '../utils/fields
|
|
1
|
+
import { unauthorizedResponse, validateApiKey } from '../utils/auth';
|
|
2
|
+
import { deflatePopulatedRelationships, deserializeFieldValue, findLocalizedFields, } from '../utils/fields';
|
|
3
3
|
const WRITE_CONFLICT_MAX_RETRIES = 3;
|
|
4
4
|
const WRITE_CONFLICT_BASE_DELAY_MS = 50;
|
|
5
5
|
function isWriteConflict(error) {
|
|
@@ -109,7 +109,24 @@ function applyTranslations({ allowedFields, data, sourceDoc, previousDoc, }) {
|
|
|
109
109
|
continue;
|
|
110
110
|
}
|
|
111
111
|
const sourceValue = source ? source[fieldName] : undefined;
|
|
112
|
-
|
|
112
|
+
let finalValue;
|
|
113
|
+
try {
|
|
114
|
+
finalValue = deserializeFieldValue(field, sourceValue, translatedValue);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
// Re-throw with the offending field name attached so the caller's
|
|
118
|
+
// logger can pinpoint which container's source-locale shape blew up
|
|
119
|
+
// (e.g. a richText with a missing `children` array, an array item
|
|
120
|
+
// without an `id`). Without this, the surface error is something like
|
|
121
|
+
// "Cannot set properties of undefined (setting '0')" with no clue
|
|
122
|
+
// which field caused it across a multi-field document.
|
|
123
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
124
|
+
const wrapped = new Error(`field "${fieldName}" failed to deserialize: ${message}`);
|
|
125
|
+
if (error instanceof Error && error.stack) {
|
|
126
|
+
wrapped.stack = error.stack;
|
|
127
|
+
}
|
|
128
|
+
throw wrapped;
|
|
129
|
+
}
|
|
113
130
|
updateData[fieldName] = finalValue;
|
|
114
131
|
acceptedFields.push(fieldName);
|
|
115
132
|
const prevValue = previous ? previous[fieldName] : undefined;
|
|
@@ -140,6 +157,7 @@ function applyTranslations({ allowedFields, data, sourceDoc, previousDoc, }) {
|
|
|
140
157
|
*/
|
|
141
158
|
async function dropUniqueCollisions(params) {
|
|
142
159
|
const { payload, collection, id, locale, fields, updateData, acceptedFields, diff } = params;
|
|
160
|
+
const dropped = [];
|
|
143
161
|
for (const field of fields) {
|
|
144
162
|
if (field.isContainer || !field.unique) {
|
|
145
163
|
continue;
|
|
@@ -169,6 +187,7 @@ async function dropUniqueCollisions(params) {
|
|
|
169
187
|
id,
|
|
170
188
|
targetLocale: locale,
|
|
171
189
|
field: field.name,
|
|
190
|
+
value,
|
|
172
191
|
collidingDocId: existing.docs[0]?.id,
|
|
173
192
|
}, '[reversia] skipping field to avoid unique collision');
|
|
174
193
|
delete updateData[field.name];
|
|
@@ -177,7 +196,9 @@ async function dropUniqueCollisions(params) {
|
|
|
177
196
|
acceptedFields.splice(acceptedIdx, 1);
|
|
178
197
|
}
|
|
179
198
|
delete diff[field.name];
|
|
199
|
+
dropped.push(field.name);
|
|
180
200
|
}
|
|
201
|
+
return dropped;
|
|
181
202
|
}
|
|
182
203
|
export function createResourcesInsertEndpoint(pluginConfig, collectionsMap, globalsMap) {
|
|
183
204
|
return {
|
|
@@ -293,7 +314,7 @@ export function createResourcesInsertEndpoint(pluginConfig, collectionsMap, glob
|
|
|
293
314
|
response.errors.push(`Item ${index}: no recognised fields in data (keys: ${Object.keys(item.data).join(', ')})`);
|
|
294
315
|
continue;
|
|
295
316
|
}
|
|
296
|
-
await dropUniqueCollisions({
|
|
317
|
+
const droppedForUnique = await dropUniqueCollisions({
|
|
297
318
|
payload: req.payload,
|
|
298
319
|
collection: slug,
|
|
299
320
|
id: itemId,
|
|
@@ -304,7 +325,7 @@ export function createResourcesInsertEndpoint(pluginConfig, collectionsMap, glob
|
|
|
304
325
|
diff,
|
|
305
326
|
});
|
|
306
327
|
if (acceptedFields.length === 0 || Object.keys(updateData).length === 0) {
|
|
307
|
-
response.errors.push(`Item ${index}: all translatable fields skipped due to unique-constraint collisions
|
|
328
|
+
response.errors.push(`Item ${index} (${item.type} ${itemId} → ${item.targetLocale}): all translatable fields skipped due to unique-constraint collisions [${droppedForUnique.join(', ')}]`);
|
|
308
329
|
continue;
|
|
309
330
|
}
|
|
310
331
|
await withRetry(() => req.payload.update({
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CollectionConfig, Endpoint } from 'payload';
|
|
2
|
-
import type { ReversiaPluginConfig } from '../types
|
|
2
|
+
import type { ReversiaPluginConfig } from '../types';
|
|
3
3
|
export declare function createResourcesSyncEndpoint(pluginConfig: ReversiaPluginConfig, _collectionsMap: Map<string, CollectionConfig>): Endpoint;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { unauthorizedResponse, validateApiKey } from '../utils/auth
|
|
2
|
-
import { decodeCursor, encodeCursor } from '../utils/cursor
|
|
3
|
-
import { parseLimit } from '../utils/payload-helpers
|
|
1
|
+
import { unauthorizedResponse, validateApiKey } from '../utils/auth';
|
|
2
|
+
import { decodeCursor, encodeCursor } from '../utils/cursor';
|
|
3
|
+
import { parseLimit } from '../utils/payload-helpers';
|
|
4
4
|
export function createResourcesSyncEndpoint(pluginConfig, _collectionsMap) {
|
|
5
5
|
return {
|
|
6
6
|
path: '/reversia/resources-sync',
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CollectionConfig, Endpoint, GlobalConfig } from 'payload';
|
|
2
|
-
import type { ReversiaPluginConfig } from '../types
|
|
2
|
+
import type { ReversiaPluginConfig } from '../types';
|
|
3
3
|
export declare function createResourcesEndpoint(pluginConfig: ReversiaPluginConfig, collectionsMap: Map<string, CollectionConfig>, globalsMap: Map<string, GlobalConfig>): Endpoint;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { unauthorizedResponse, validateApiKey } from '../utils/auth
|
|
2
|
-
import { decodeCursor, encodeCursor } from '../utils/cursor
|
|
3
|
-
import { findLocalizedFields, serializeField } from '../utils/fields
|
|
4
|
-
import { parseLimit, resolveDefaultLocale } from '../utils/payload-helpers
|
|
1
|
+
import { unauthorizedResponse, validateApiKey } from '../utils/auth';
|
|
2
|
+
import { decodeCursor, encodeCursor } from '../utils/cursor';
|
|
3
|
+
import { findLocalizedFields, serializeField } from '../utils/fields';
|
|
4
|
+
import { parseLimit, resolveDefaultLocale } from '../utils/payload-helpers';
|
|
5
5
|
function extractContent(doc, fields) {
|
|
6
6
|
const content = {};
|
|
7
7
|
const contentTypes = {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { triggerCrawl } from '../trigger-crawl';
|
|
2
|
+
export function createTriggerCrawlDashboardEndpoint(pluginConfig) {
|
|
3
|
+
return {
|
|
4
|
+
path: '/reversia/dashboard/trigger-crawl',
|
|
5
|
+
method: 'post',
|
|
6
|
+
handler: async (req) => {
|
|
7
|
+
if (!req.user) {
|
|
8
|
+
return Response.json({ error: 'Unauthorized' }, { status: 401 });
|
|
9
|
+
}
|
|
10
|
+
const body = req.json ? await req.json() : undefined;
|
|
11
|
+
const types = Array.isArray(body?.types)
|
|
12
|
+
? body.types.filter((t) => typeof t === 'string')
|
|
13
|
+
: undefined;
|
|
14
|
+
const noCache = Boolean(body?.noCache);
|
|
15
|
+
try {
|
|
16
|
+
const result = await triggerCrawl({
|
|
17
|
+
apiKey: pluginConfig.apiKey,
|
|
18
|
+
baseUrl: pluginConfig.baseUrl,
|
|
19
|
+
...(types && types.length > 0 ? { types } : {}),
|
|
20
|
+
...(noCache ? { noCache: true } : {}),
|
|
21
|
+
});
|
|
22
|
+
return Response.json(result);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
26
|
+
req.payload.logger.error({
|
|
27
|
+
msg: '[reversia] dashboard trigger-crawl failed',
|
|
28
|
+
err: message,
|
|
29
|
+
});
|
|
30
|
+
return Response.json({ error: message }, { status: 502 });
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Config } from 'payload';
|
|
2
|
-
import type { ReversiaPluginConfig } from './types
|
|
3
|
-
export type {
|
|
4
|
-
export {
|
|
2
|
+
import type { ReversiaPluginConfig } from './types';
|
|
3
|
+
export type { TriggerCrawlOptions, TriggerCrawlResult } from './trigger-crawl';
|
|
4
|
+
export { triggerCrawl } from './trigger-crawl';
|
|
5
|
+
export type { ConfirmResourcesSyncResponse, InsertionRequest, InsertionResponse, ResourceDefinition, ResourceItem, ResourceResponse, ReversiaErrorResponse, ReversiaFieldCustom, ReversiaPluginConfig, SettingsResponse, StreamResponse, TranslatableFieldConfig, } from './types';
|
|
6
|
+
export { ReversiaFieldBehavior, ReversiaFieldType } from './types';
|
|
5
7
|
export declare const reversiaPlugin: (pluginConfig: ReversiaPluginConfig) => (config: Config) => Config;
|
|
6
8
|
export default reversiaPlugin;
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { reversiaSyncPendingCollection } from './collections/sync-pending
|
|
2
|
-
import { createConfirmResourcesSyncEndpoint } from './endpoints/confirm-resources-sync
|
|
3
|
-
import { createResourceEndpoint } from './endpoints/resource
|
|
4
|
-
import { createResourcesEndpoint } from './endpoints/resources
|
|
5
|
-
import { createResourcesDefinitionEndpoint } from './endpoints/resources-definition
|
|
6
|
-
import { createResourcesInsertEndpoint } from './endpoints/resources-insert
|
|
7
|
-
import { createResourcesSyncEndpoint } from './endpoints/resources-sync
|
|
8
|
-
import { createSettingsEndpoint } from './endpoints/settings
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
1
|
+
import { reversiaSyncPendingCollection } from './collections/sync-pending';
|
|
2
|
+
import { createConfirmResourcesSyncEndpoint } from './endpoints/confirm-resources-sync';
|
|
3
|
+
import { createResourceEndpoint } from './endpoints/resource';
|
|
4
|
+
import { createResourcesEndpoint } from './endpoints/resources';
|
|
5
|
+
import { createResourcesDefinitionEndpoint } from './endpoints/resources-definition';
|
|
6
|
+
import { createResourcesInsertEndpoint } from './endpoints/resources-insert';
|
|
7
|
+
import { createResourcesSyncEndpoint } from './endpoints/resources-sync';
|
|
8
|
+
import { createSettingsEndpoint } from './endpoints/settings';
|
|
9
|
+
import { createTriggerCrawlDashboardEndpoint } from './endpoints/trigger-crawl-dashboard';
|
|
10
|
+
import { createAfterChangeHook } from './hooks/after-change';
|
|
11
|
+
import { findLocalizedFields } from './utils/fields';
|
|
12
|
+
export { triggerCrawl } from './trigger-crawl';
|
|
13
|
+
export { ReversiaFieldBehavior, ReversiaFieldType } from './types';
|
|
12
14
|
const APPLIED_MARKER = Symbol.for('payload-plugin-reversia.applied');
|
|
13
15
|
export const reversiaPlugin = (pluginConfig) => (config) => {
|
|
14
16
|
if (pluginConfig.disabled) {
|
|
@@ -76,6 +78,7 @@ export const reversiaPlugin = (pluginConfig) => (config) => {
|
|
|
76
78
|
createResourcesInsertEndpoint(pluginConfig, collectionsMap, globalsMap),
|
|
77
79
|
createConfirmResourcesSyncEndpoint(pluginConfig),
|
|
78
80
|
createSettingsEndpoint(pluginConfig),
|
|
81
|
+
createTriggerCrawlDashboardEndpoint(pluginConfig),
|
|
79
82
|
];
|
|
80
83
|
return config;
|
|
81
84
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CollectionSlug, GlobalSlug } from 'payload';
|
|
2
|
+
export interface TriggerCrawlOptions {
|
|
3
|
+
/** Reversia project API key (the same value passed to `reversiaPlugin({ apiKey })`). */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/**
|
|
6
|
+
* Collection and/or global slugs to crawl. Each slug is prefixed with
|
|
7
|
+
* `payloadcms:` before being sent to Reversia. Omit/empty → crawl all
|
|
8
|
+
* enabled resources for the project.
|
|
9
|
+
*/
|
|
10
|
+
types?: Array<CollectionSlug | GlobalSlug>;
|
|
11
|
+
/** Bypass cached crawl content. Defaults to `false`. */
|
|
12
|
+
noCache?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Override the Reversia API base URL. Defaults to `process.env.REVERSIA_API_URL`
|
|
15
|
+
* then a built-in production/staging URL.
|
|
16
|
+
*/
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
/** Optional custom fetch (for tests / proxies). Defaults to the global `fetch`. */
|
|
19
|
+
fetch?: typeof fetch;
|
|
20
|
+
}
|
|
21
|
+
export interface TriggerCrawlResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function triggerCrawl(options: TriggerCrawlOptions): Promise<TriggerCrawlResult>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const DEFAULT_BASE_URL = 'https://staging.api.reversia.tech';
|
|
2
|
+
const TYPE_NAMESPACE = 'payloadcms:';
|
|
3
|
+
const TRIGGER_CRAWL_PATH = '/projects/trigger-crawl';
|
|
4
|
+
export async function triggerCrawl(options) {
|
|
5
|
+
if (typeof options.apiKey !== 'string' || options.apiKey.length === 0) {
|
|
6
|
+
throw new Error('[reversia] apiKey is required. Pass `TriggerCrawlOptions.apiKey` as a non-empty string.');
|
|
7
|
+
}
|
|
8
|
+
const baseUrl = (options.baseUrl ?? process.env.REVERSIA_API_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
9
|
+
const normalizedTypes = (options.types ?? [])
|
|
10
|
+
.filter((t) => typeof t === 'string' && t.length > 0)
|
|
11
|
+
.map((t) => `${TYPE_NAMESPACE}${t}`);
|
|
12
|
+
const body = {};
|
|
13
|
+
if (normalizedTypes.length > 0) {
|
|
14
|
+
body.types = normalizedTypes;
|
|
15
|
+
}
|
|
16
|
+
if (options.noCache) {
|
|
17
|
+
body.noCache = true;
|
|
18
|
+
}
|
|
19
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
20
|
+
const response = await fetchImpl(`${baseUrl}${TRIGGER_CRAWL_PATH}`, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
Accept: 'application/json',
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(body),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
let excerpt = '';
|
|
31
|
+
try {
|
|
32
|
+
excerpt = (await response.text()).slice(0, 200);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// ignore body read errors — keep the status info
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`[reversia] trigger-crawl failed: ${response.status} ${response.statusText}${excerpt ? ` — ${excerpt}` : ''}`);
|
|
38
|
+
}
|
|
39
|
+
let parsed = null;
|
|
40
|
+
try {
|
|
41
|
+
parsed = (await response.json());
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
parsed = null;
|
|
45
|
+
}
|
|
46
|
+
return { success: parsed?.success !== false };
|
|
47
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CollectionSlug } from 'payload';
|
|
2
|
-
import type { LeafSegment } from './utils/path-resolver
|
|
2
|
+
import type { LeafSegment } from './utils/path-resolver';
|
|
3
3
|
export interface ReversiaPluginConfig {
|
|
4
4
|
/**
|
|
5
5
|
* API key used by Reversia SaaS to authenticate requests.
|
|
@@ -20,6 +20,12 @@ export interface ReversiaPluginConfig {
|
|
|
20
20
|
* Whether the plugin is disabled. Defaults to false.
|
|
21
21
|
*/
|
|
22
22
|
disabled?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Override the Reversia API base URL used by `triggerCrawl` and the
|
|
25
|
+
* dashboard button. Defaults to `process.env.REVERSIA_API_URL` then the
|
|
26
|
+
* built-in production URL.
|
|
27
|
+
*/
|
|
28
|
+
baseUrl?: string;
|
|
23
29
|
}
|
|
24
30
|
export declare enum ReversiaFieldType {
|
|
25
31
|
TEXT = "TEXT",
|
package/dist/utils/cursor.d.ts
CHANGED
package/dist/utils/fields.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Field } from 'payload';
|
|
2
|
-
import type { LocalizedFieldInfo, TranslatableFieldConfig } from '../types
|
|
3
|
-
import { ReversiaFieldType } from '../types
|
|
2
|
+
import type { LocalizedFieldInfo, TranslatableFieldConfig } from '../types';
|
|
3
|
+
import { ReversiaFieldType } from '../types';
|
|
4
4
|
/**
|
|
5
5
|
* Collects one `LocalizedFieldInfo` per top-level field that is itself
|
|
6
6
|
* localized OR that contains at least one localized descendant.
|
package/dist/utils/fields.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ReversiaFieldType } from '../types
|
|
2
|
-
import { applyByKeys, compileKeyMatcher, DEFAULT_RICHTEXT_KEYS, extractByKeys, } from './json-extract
|
|
3
|
-
import { resolveStaticLabel } from './labels
|
|
4
|
-
import { applyTranslationsToContainer, joinPointers, resolveLeafLocations, } from './path-resolver
|
|
1
|
+
import { ReversiaFieldType } from '../types';
|
|
2
|
+
import { applyByKeys, compileKeyMatcher, DEFAULT_RICHTEXT_KEYS, extractByKeys, } from './json-extract';
|
|
3
|
+
import { resolveStaticLabel } from './labels';
|
|
4
|
+
import { applyTranslationsToContainer, joinPointers, resolveLeafLocations, } from './path-resolver';
|
|
5
5
|
function getFieldLabel(field) {
|
|
6
6
|
if (!('name' in field)) {
|
|
7
7
|
return '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sequoialabs/payload-plugin-reversia",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Jean Walrave",
|
|
6
6
|
"email": "contact@reversia.tech",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
".": {
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
29
|
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./client": {
|
|
32
|
+
"types": "./dist/client/index.d.ts",
|
|
33
|
+
"import": "./dist/client/index.js"
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
36
|
"main": "dist/index.js",
|
|
@@ -44,14 +48,17 @@
|
|
|
44
48
|
"prepublishOnly": "bun run test && bun run build"
|
|
45
49
|
},
|
|
46
50
|
"peerDependencies": {
|
|
47
|
-
"payload": "^3.0.0"
|
|
51
|
+
"payload": "^3.0.0",
|
|
52
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
48
53
|
},
|
|
49
54
|
"devDependencies": {
|
|
50
55
|
"@biomejs/biome": "2.4.12",
|
|
51
56
|
"@payloadcms/db-sqlite": "^3.0.0",
|
|
52
57
|
"@payloadcms/richtext-lexical": "^3.79.1",
|
|
53
58
|
"@types/bun": "^1.3.10",
|
|
59
|
+
"@types/react": "^19.0.0",
|
|
54
60
|
"payload": "^3.0.0",
|
|
61
|
+
"react": "^19.0.0",
|
|
55
62
|
"typescript": "^5.0.0"
|
|
56
63
|
}
|
|
57
|
-
}
|
|
64
|
+
}
|