@voyantjs/plugin-payload-cms 0.5.0 → 0.6.2

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 CHANGED
@@ -1,6 +1,15 @@
1
1
  # @voyantjs/plugin-payload-cms
2
2
 
3
- Payload CMS sync plugin for Voyant. Subscribes to module events and mirrors documents into a Payload collection keyed by a `voyantId` field.
3
+ Payload CMS sync adapter bundle for Voyant.
4
+
5
+ Architecturally, this package is primarily:
6
+
7
+ - a Payload sync adapter
8
+ - a subscriber bundle that mirrors Voyant module records into Payload
9
+ - an optional packaged bundle when an app wants one installable entrypoint
10
+
11
+ It subscribes to module events and mirrors documents into a Payload collection
12
+ keyed by a `voyantId` field.
4
13
 
5
14
  ## Install
6
15
 
@@ -14,28 +23,32 @@ pnpm add @voyantjs/plugin-payload-cms
14
23
  import { payloadCmsPlugin } from "@voyantjs/plugin-payload-cms"
15
24
  import { createApp } from "@voyantjs/hono"
16
25
 
26
+ const payloadCmsSync = payloadCmsPlugin({
27
+ apiUrl: "https://cms.example.com/api",
28
+ apiKey: env.PAYLOAD_API_KEY,
29
+ collection: "products",
30
+ // optional: events, mapEvent, logger, apiKeyAuthScheme
31
+ })
32
+
17
33
  const app = createApp({
18
- plugins: [
19
- payloadCmsPlugin({
20
- apiUrl: "https://cms.example.com/api",
21
- apiKey: env.PAYLOAD_API_KEY,
22
- collection: "products",
23
- // optional: events, mapEvent, logger, apiKeyAuthScheme
24
- }),
25
- ],
34
+ plugins: [payloadCmsSync],
26
35
  })
27
36
  ```
28
37
 
29
- By default the plugin wires up 3 subscribers (`product.created`, `product.updated`, `product.deleted`) that upsert/delete documents keyed by `voyantId`. All error handling is fire-and-forget per the EventBus contract.
38
+ `payloadCmsPlugin(...)` is the packaged distribution helper. The runtime role is
39
+ still a subscriber-driven Payload sync adapter. By default it wires up 3
40
+ subscribers (`product.created`, `product.updated`, `product.deleted`) that
41
+ upsert/delete documents keyed by `voyantId`. All error handling is
42
+ fire-and-forget per the EventBus contract.
30
43
 
31
44
  ## Exports
32
45
 
33
46
  | Entry | Description |
34
47
  | --- | --- |
35
48
  | `.` | Barrel re-exports |
36
- | `./plugin` | `payloadCmsPlugin(options)` |
49
+ | `./plugin` | `payloadCmsPlugin(options)` — packaged adapter/subscriber bundle |
37
50
  | `./client` | `createPayloadClient` — `upsertByVoyantId`, `deleteByVoyantId`, `findByVoyantId` |
38
- | `./types` | Plugin option types |
51
+ | `./types` | Adapter and bundle option types |
39
52
 
40
53
  ## License
41
54
 
package/dist/index.d.ts CHANGED
@@ -2,5 +2,7 @@ export type { PayloadClient, PayloadClientOptions } from "./client.js";
2
2
  export { createPayloadClient } from "./client.js";
3
3
  export type { PayloadCmsPluginOptions, PayloadLogger, PayloadMapFn, PayloadSyncEventNames, } from "./plugin.js";
4
4
  export { payloadCmsPlugin } from "./plugin.js";
5
+ export type { PayloadSyncRuntime, ResolvedPayloadSyncEventNames } from "./runtime.js";
6
+ export { createPayloadSyncRuntime } from "./runtime.js";
5
7
  export type { PayloadDocBody, PayloadFetch, VoyantEntityEvent } from "./types.js";
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EACV,uBAAuB,EACvB,aAAa,EACb,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EACV,uBAAuB,EACvB,aAAa,EACb,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,YAAY,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { createPayloadClient } from "./client.js";
2
2
  export { payloadCmsPlugin } from "./plugin.js";
3
+ export { createPayloadSyncRuntime } from "./runtime.js";
package/dist/plugin.d.ts CHANGED
@@ -1,59 +1,21 @@
1
1
  import type { Plugin } from "@voyantjs/core";
2
- import { type PayloadClientOptions } from "./client.js";
2
+ import type { PayloadClientOptions } from "./client.js";
3
3
  import type { PayloadDocBody, VoyantEntityEvent } from "./types.js";
4
- /**
5
- * Event names the plugin subscribes to. Defaults match the `<resource>.<pastTenseAction>`
6
- * naming convention documented by the EventBus contract.
7
- */
8
4
  export interface PayloadSyncEventNames {
9
5
  created?: string;
10
6
  updated?: string;
11
7
  deleted?: string;
12
8
  }
13
- /**
14
- * Mapper from a Voyant event payload (assumed to carry at least `id: string`)
15
- * to a Payload document body. The `voyantId` field is injected automatically
16
- * by the client — do not set it here.
17
- */
18
9
  export type PayloadMapFn = (event: VoyantEntityEvent) => PayloadDocBody;
19
- /**
20
- * Logger shape used to surface plugin errors without coupling to any specific
21
- * runtime. Defaults to `console`.
22
- */
23
10
  export interface PayloadLogger {
24
11
  error: (message: string, meta?: unknown) => void;
25
12
  info?: (message: string, meta?: unknown) => void;
26
13
  }
27
14
  export interface PayloadCmsPluginOptions extends PayloadClientOptions {
28
- /**
29
- * Payload collection slug to sync Voyant records into (e.g. "products").
30
- */
31
15
  collection: string;
32
- /**
33
- * Event names this plugin subscribes to. Defaults to
34
- * `product.created` / `product.updated` / `product.deleted`.
35
- */
36
16
  events?: PayloadSyncEventNames;
37
- /**
38
- * Map a Voyant event payload into a Payload document body. Defaults to
39
- * passing through every property except `id` (which becomes `voyantId`).
40
- */
41
17
  mapEvent?: PayloadMapFn;
42
- /** Override logger. Defaults to `console`. */
43
18
  logger?: PayloadLogger;
44
19
  }
45
- /**
46
- * Build a Voyant {@link Plugin} that pushes event payloads to a Payload
47
- * collection as documents, keyed by `voyantId` for idempotent upserts.
48
- *
49
- * The plugin subscribes to three events (configurable via
50
- * {@link PayloadCmsPluginOptions.events}):
51
- * - `product.created` → upsert document
52
- * - `product.updated` → upsert document
53
- * - `product.deleted` → delete document by `voyantId`
54
- *
55
- * Errors are caught and logged — subscribers are fire-and-forget per the
56
- * EventBus contract, so a Payload outage never blocks the emitter.
57
- */
58
20
  export declare function payloadCmsPlugin(options: PayloadCmsPluginOptions): Plugin;
59
21
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,gBAAgB,CAAA;AAExD,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,cAAc,CAAA;AAEvE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAChD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,qBAAqB,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,aAAa,CAAA;CACvB;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CA+DzE"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,gBAAgB,CAAA;AAGxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEvD,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGnE,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,cAAc,CAAA;AAEvE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAChD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,qBAAqB,CAAA;IAC9B,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,MAAM,CAAC,EAAE,aAAa,CAAA;CACvB;AASD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAyDzE"}
package/dist/plugin.js CHANGED
@@ -1,8 +1,6 @@
1
- import { createPayloadClient } from "./client.js";
2
- function defaultMapEvent(event) {
3
- const { id: _id, ...rest } = event;
4
- return rest;
5
- }
1
+ import { ZodError } from "zod";
2
+ import { createPayloadSyncRuntime } from "./runtime.js";
3
+ import { payloadCmsPluginOptionsSchema } from "./validation.js";
6
4
  function coerceEvent(data) {
7
5
  if (data == null || typeof data !== "object")
8
6
  return null;
@@ -11,37 +9,18 @@ function coerceEvent(data) {
11
9
  return null;
12
10
  return maybe;
13
11
  }
14
- /**
15
- * Build a Voyant {@link Plugin} that pushes event payloads to a Payload
16
- * collection as documents, keyed by `voyantId` for idempotent upserts.
17
- *
18
- * The plugin subscribes to three events (configurable via
19
- * {@link PayloadCmsPluginOptions.events}):
20
- * - `product.created` → upsert document
21
- * - `product.updated` → upsert document
22
- * - `product.deleted` → delete document by `voyantId`
23
- *
24
- * Errors are caught and logged — subscribers are fire-and-forget per the
25
- * EventBus contract, so a Payload outage never blocks the emitter.
26
- */
27
12
  export function payloadCmsPlugin(options) {
28
- const client = createPayloadClient(options);
29
- const mapEvent = options.mapEvent ?? defaultMapEvent;
30
- const logger = options.logger ?? console;
31
- const eventNames = {
32
- created: options.events?.created ?? "product.created",
33
- updated: options.events?.updated ?? "product.updated",
34
- deleted: options.events?.deleted ?? "product.deleted",
35
- };
13
+ const validatedOptions = parsePayloadCmsPluginOptions(options);
14
+ const { client, mapEvent, logger, eventNames } = createPayloadSyncRuntime(validatedOptions);
36
15
  const subscribers = [
37
16
  {
38
17
  event: eventNames.created,
39
- handler: async (data) => {
40
- const event = coerceEvent(data);
18
+ handler: async (envelope) => {
19
+ const event = coerceEvent(envelope.data);
41
20
  if (!event)
42
21
  return;
43
22
  try {
44
- await client.upsertByVoyantId(options.collection, event.id, mapEvent(event));
23
+ await client.upsertByVoyantId(validatedOptions.collection, event.id, mapEvent(event));
45
24
  }
46
25
  catch (err) {
47
26
  logger.error(`[payload-cms] upsert on "${eventNames.created}" failed for ${event.id}`, err);
@@ -50,12 +29,12 @@ export function payloadCmsPlugin(options) {
50
29
  },
51
30
  {
52
31
  event: eventNames.updated,
53
- handler: async (data) => {
54
- const event = coerceEvent(data);
32
+ handler: async (envelope) => {
33
+ const event = coerceEvent(envelope.data);
55
34
  if (!event)
56
35
  return;
57
36
  try {
58
- await client.upsertByVoyantId(options.collection, event.id, mapEvent(event));
37
+ await client.upsertByVoyantId(validatedOptions.collection, event.id, mapEvent(event));
59
38
  }
60
39
  catch (err) {
61
40
  logger.error(`[payload-cms] upsert on "${eventNames.updated}" failed for ${event.id}`, err);
@@ -64,12 +43,12 @@ export function payloadCmsPlugin(options) {
64
43
  },
65
44
  {
66
45
  event: eventNames.deleted,
67
- handler: async (data) => {
68
- const event = coerceEvent(data);
46
+ handler: async (envelope) => {
47
+ const event = coerceEvent(envelope.data);
69
48
  if (!event)
70
49
  return;
71
50
  try {
72
- await client.deleteByVoyantId(options.collection, event.id);
51
+ await client.deleteByVoyantId(validatedOptions.collection, event.id);
73
52
  }
74
53
  catch (err) {
75
54
  logger.error(`[payload-cms] delete on "${eventNames.deleted}" failed for ${event.id}`, err);
@@ -83,3 +62,20 @@ export function payloadCmsPlugin(options) {
83
62
  subscribers,
84
63
  };
85
64
  }
65
+ function parsePayloadCmsPluginOptions(options) {
66
+ try {
67
+ return payloadCmsPluginOptionsSchema.parse(options);
68
+ }
69
+ catch (error) {
70
+ if (error instanceof ZodError) {
71
+ const detail = error.issues
72
+ .map((issue) => {
73
+ const path = issue.path.join(".") || "options";
74
+ return `${path}: ${issue.message}`;
75
+ })
76
+ .join("; ");
77
+ throw new Error(`Invalid Payload CMS plugin options: ${detail}`);
78
+ }
79
+ throw error;
80
+ }
81
+ }
@@ -0,0 +1,15 @@
1
+ import { createPayloadClient } from "./client.js";
2
+ import type { PayloadCmsPluginOptions, PayloadLogger, PayloadMapFn } from "./plugin.js";
3
+ export interface ResolvedPayloadSyncEventNames {
4
+ created: string;
5
+ updated: string;
6
+ deleted: string;
7
+ }
8
+ export interface PayloadSyncRuntime {
9
+ client: ReturnType<typeof createPayloadClient>;
10
+ logger: PayloadLogger;
11
+ mapEvent: PayloadMapFn;
12
+ eventNames: ResolvedPayloadSyncEventNames;
13
+ }
14
+ export declare function createPayloadSyncRuntime(options: PayloadCmsPluginOptions): PayloadSyncRuntime;
15
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EAAE,uBAAuB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAGvF,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA;IAC9C,MAAM,EAAE,aAAa,CAAA;IACrB,QAAQ,EAAE,YAAY,CAAA;IACtB,UAAU,EAAE,6BAA6B,CAAA;CAC1C;AAOD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,uBAAuB,GAAG,kBAAkB,CAW7F"}
@@ -0,0 +1,17 @@
1
+ import { createPayloadClient } from "./client.js";
2
+ function defaultMapEvent(event) {
3
+ const { id: _id, ...rest } = event;
4
+ return rest;
5
+ }
6
+ export function createPayloadSyncRuntime(options) {
7
+ return {
8
+ client: createPayloadClient(options),
9
+ logger: options.logger ?? console,
10
+ mapEvent: options.mapEvent ?? defaultMapEvent,
11
+ eventNames: {
12
+ created: options.events?.created ?? "product.created",
13
+ updated: options.events?.updated ?? "product.updated",
14
+ deleted: options.events?.deleted ?? "product.deleted",
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ import type { PayloadLogger, PayloadMapFn, PayloadSyncEventNames } from "./plugin.js";
3
+ import type { PayloadFetch } from "./types.js";
4
+ export declare const payloadCmsPluginOptionsSchema: z.ZodObject<{
5
+ apiUrl: z.ZodString;
6
+ apiKey: z.ZodString;
7
+ collection: z.ZodString;
8
+ voyantIdField: z.ZodOptional<z.ZodString>;
9
+ apiKeyAuthScheme: z.ZodOptional<z.ZodString>;
10
+ fetch: z.ZodOptional<z.ZodCustom<PayloadFetch | undefined, PayloadFetch | undefined>>;
11
+ events: z.ZodOptional<z.ZodCustom<PayloadSyncEventNames | undefined, PayloadSyncEventNames | undefined>>;
12
+ mapEvent: z.ZodOptional<z.ZodCustom<PayloadMapFn | undefined, PayloadMapFn | undefined>>;
13
+ logger: z.ZodOptional<z.ZodCustom<PayloadLogger | undefined, PayloadLogger | undefined>>;
14
+ }, z.core.$strip>;
15
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAEV,aAAa,EACb,YAAY,EACZ,qBAAqB,EACtB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAkC9C,eAAO,MAAM,6BAA6B;;;;;;;;;;iBAUK,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ const optionalString = z.string().trim().min(1).optional();
3
+ const optionalFetch = z.custom((value) => value === undefined || typeof value === "function", "Expected a fetch implementation function");
4
+ const optionalLogger = z.custom((value) => value === undefined ||
5
+ (typeof value === "object" &&
6
+ value !== null &&
7
+ typeof value.error === "function" &&
8
+ ((value.info ?? undefined) === undefined ||
9
+ typeof value.info === "function")), "Expected a logger with an error function");
10
+ const optionalMapEvent = z.custom((value) => value === undefined || typeof value === "function", "Expected a mapEvent function");
11
+ const optionalEvents = z.custom((value) => {
12
+ if (value === undefined)
13
+ return true;
14
+ if (typeof value !== "object" || value === null)
15
+ return false;
16
+ const events = value;
17
+ return [events.created, events.updated, events.deleted].every((entry) => entry === undefined || (typeof entry === "string" && entry.trim().length > 0));
18
+ }, "Expected event names to be non-empty strings");
19
+ export const payloadCmsPluginOptionsSchema = z.object({
20
+ apiUrl: z.string().trim().url(),
21
+ apiKey: z.string().trim().min(1),
22
+ collection: z.string().trim().min(1),
23
+ voyantIdField: optionalString,
24
+ apiKeyAuthScheme: optionalString,
25
+ fetch: optionalFetch.optional(),
26
+ events: optionalEvents.optional(),
27
+ mapEvent: optionalMapEvent.optional(),
28
+ logger: optionalLogger.optional(),
29
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/plugin-payload-cms",
3
- "version": "0.5.0",
3
+ "version": "0.6.2",
4
4
  "license": "FSL-1.1-Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -22,7 +22,8 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "@voyantjs/core": "0.5.0"
25
+ "zod": "^4.3.6",
26
+ "@voyantjs/core": "0.6.2"
26
27
  },
27
28
  "devDependencies": {
28
29
  "typescript": "^6.0.2",