@velocms/plugin-sdk 1.0.0-alpha.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.
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/test-helpers.ts
21
+ var test_helpers_exports = {};
22
+ __export(test_helpers_exports, {
23
+ mockVelocms: () => mockVelocms,
24
+ resetMockContext: () => resetMockContext
25
+ });
26
+ module.exports = __toCommonJS(test_helpers_exports);
27
+ function mockVelocms(opts = {}) {
28
+ const fetchCalls = [];
29
+ const logs = [];
30
+ const kvStore = { ...opts.kvStore ?? {} };
31
+ const emittedEvents = [];
32
+ const eventHandlers = /* @__PURE__ */ new Map();
33
+ const onEvent = ((eventName, handler) => {
34
+ let set = eventHandlers.get(eventName);
35
+ if (!set) {
36
+ set = /* @__PURE__ */ new Set();
37
+ eventHandlers.set(eventName, set);
38
+ }
39
+ set.add(handler);
40
+ return () => {
41
+ eventHandlers.get(eventName)?.delete(handler);
42
+ };
43
+ });
44
+ const emitEvent = async (eventName, payload) => {
45
+ emittedEvents.push({ eventName, payload });
46
+ const handlers = eventHandlers.get(eventName);
47
+ if (handlers) {
48
+ for (const handler of handlers) {
49
+ await handler(payload);
50
+ }
51
+ }
52
+ };
53
+ const events = { on: onEvent, emit: emitEvent };
54
+ const fetchQueue = Array.isArray(opts.fetchResponse) ? [...opts.fetchResponse] : opts.fetchResponse ? [opts.fetchResponse] : [];
55
+ const defaultFetchResponse = { ok: true, status: 200, data: {} };
56
+ const mockFetch = (input, init) => {
57
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
58
+ fetchCalls.push({ url, options: init });
59
+ const response = fetchQueue.length > 0 ? fetchQueue.shift() ?? defaultFetchResponse : defaultFetchResponse;
60
+ const body = response.text ?? JSON.stringify(response.data ?? {});
61
+ const status = response.status ?? (response.ok ? 200 : 400);
62
+ return Promise.resolve(
63
+ new Response(body, {
64
+ status,
65
+ headers: { "Content-Type": "application/json" }
66
+ })
67
+ );
68
+ };
69
+ const tenant = {
70
+ id: opts.tenant?.id ?? "test-tenant-id",
71
+ slug: opts.tenant?.slug ?? "test-blog",
72
+ locale: opts.tenant?.locale ?? "en-US"
73
+ };
74
+ const settings = {
75
+ blogName: opts.settings?.blogName ?? "Test Blog",
76
+ siteUrl: opts.settings?.siteUrl ?? "https://test.velocms.org",
77
+ theme: opts.settings?.theme ?? "default",
78
+ plan: opts.settings?.plan ?? "pro"
79
+ };
80
+ const ctx = {
81
+ tenant,
82
+ settings,
83
+ fetch: mockFetch,
84
+ log: {
85
+ info: (message, data) => logs.push({ level: "info", message, data }),
86
+ warn: (message, data) => logs.push({ level: "warn", message, data }),
87
+ error: (message, data) => logs.push({ level: "error", message, data })
88
+ },
89
+ kv: {
90
+ get: (key) => Promise.resolve(kvStore[key] ?? null),
91
+ set: (key, value) => {
92
+ kvStore[key] = value;
93
+ return Promise.resolve();
94
+ },
95
+ delete: (key) => {
96
+ delete kvStore[key];
97
+ return Promise.resolve();
98
+ }
99
+ },
100
+ events,
101
+ _fetchCalls: fetchCalls,
102
+ _logs: logs,
103
+ _kvStore: kvStore,
104
+ _emittedEvents: emittedEvents,
105
+ _dispatchSystemEvent: async (eventName, payload) => {
106
+ const handlers = eventHandlers.get(eventName);
107
+ if (handlers) {
108
+ for (const handler of handlers) {
109
+ await handler(payload);
110
+ }
111
+ }
112
+ }
113
+ };
114
+ return ctx;
115
+ }
116
+ function resetMockContext(ctx) {
117
+ ctx._fetchCalls.length = 0;
118
+ ctx._logs.length = 0;
119
+ ctx._emittedEvents.length = 0;
120
+ }
121
+ // Annotate the CommonJS export names for ESM import in node:
122
+ 0 && (module.exports = {
123
+ mockVelocms,
124
+ resetMockContext
125
+ });
126
+ //# sourceMappingURL=test-helpers.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/test-helpers.ts"],"sourcesContent":["/**\n * VeloCMS Plugin SDK — Test Helpers\n *\n * Provides mock implementations of the host bridge contract so plugin authors\n * can write unit tests without a running VeloCMS instance.\n *\n * @example\n * ```typescript\n * import { mockVelocms } from \"@velocms/plugin-sdk/test-helpers\";\n *\n * const ctx = mockVelocms({\n * tenant: { id: \"t1\", slug: \"my-blog\", locale: \"en-US\" },\n * fetchResponse: { ok: true, data: { result: \"ok\" } },\n * });\n *\n * await myPlugin.handlePostPublish({ post: { slug: \"hello\" } }, ctx);\n * expect(ctx._fetchCalls).toHaveLength(1);\n * expect(ctx._fetchCalls[0].url).toBe(\"https://hooks.slack.com/...\");\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { HookContext, ContextTenant, ContextSettings } from \"./types/context\";\nimport type {\n EventBusContext,\n EventName,\n SystemEventName,\n PluginEventName,\n SystemEventPayloadMap,\n} from \"./types/events\";\n\n// ---------------------------------------------------------------------------\n// Mock configuration types\n// ---------------------------------------------------------------------------\n\nexport interface MockFetchResponse {\n ok: boolean;\n status?: number;\n /** JSON body returned by the mock */\n data?: unknown;\n text?: string;\n}\n\nexport interface MockVelocmsOptions {\n tenant?: Partial<ContextTenant>;\n settings?: Partial<ContextSettings>;\n /**\n * Static response for all fetch() calls.\n * Pass an array to return different responses for successive calls.\n */\n fetchResponse?: MockFetchResponse | MockFetchResponse[];\n /**\n * Pre-populated key-value store entries.\n */\n kvStore?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Instrumented context (extends HookContext with test introspection)\n// ---------------------------------------------------------------------------\n\nexport interface MockFetchCall {\n url: string;\n options?: RequestInit;\n}\n\n/** A custom event recorded when the plugin under test calls `ctx.events.emit()`. */\nexport interface MockEmittedEvent {\n eventName: PluginEventName;\n payload: Record<string, unknown>;\n}\n\nexport interface MockHookContext extends HookContext {\n /** All fetch() calls made during the test */\n _fetchCalls: MockFetchCall[];\n /** All log messages emitted during the test */\n _logs: Array<{ level: \"info\" | \"warn\" | \"error\"; message: string; data?: Record<string, unknown> }>;\n /** Current key-value store state */\n _kvStore: Record<string, string>;\n /** All events emitted during the test via `ctx.events.emit()` */\n _emittedEvents: MockEmittedEvent[];\n /**\n * Test-only helper: simulate VeloCMS core firing a system event, invoking\n * every handler the plugin under test registered via `ctx.events.on(...)`.\n *\n * Not part of the real `HookContext` — the live event bus fires these\n * automatically; in a unit test there's no running VeloCMS instance to\n * fire them for you.\n *\n * @example\n * ```typescript\n * const ctx = mockVelocms();\n * await myPlugin.onActivate(ctx); // registers ctx.events.on(\"post.published\", ...)\n * await ctx._dispatchSystemEvent(\"post.published\", { post: { id: \"1\", title: \"Hi\" } });\n * ```\n */\n _dispatchSystemEvent<E extends SystemEventName>(\n eventName: E,\n payload: SystemEventPayloadMap[E]\n ): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a mock `HookContext` for unit testing plugin hook handlers.\n *\n * The returned context is a fully-instrumented stand-in that:\n * - Records all `ctx.fetch()` calls in `_fetchCalls`\n * - Records all `ctx.log.*()` calls in `_logs`\n * - Provides an in-memory `ctx.kv` store (seeded from `opts.kvStore`)\n * - Returns controlled responses from `opts.fetchResponse`\n *\n * No network requests are made. No VeloCMS instance is required.\n *\n * @param opts - Optional overrides for tenant, settings, fetch responses, kv seed\n * @returns - Instrumented `MockHookContext` compatible with `HookContext`\n */\nexport function mockVelocms(opts: MockVelocmsOptions = {}): MockHookContext {\n const fetchCalls: MockFetchCall[] = [];\n const logs: MockHookContext[\"_logs\"] = [];\n const kvStore: Record<string, string> = { ...(opts.kvStore ?? {}) };\n const emittedEvents: MockEmittedEvent[] = [];\n\n // In-memory pub/sub for ctx.events — mirrors the real EventBusContext\n // shape without a running VeloCMS instance.\n const eventHandlers = new Map<EventName, Set<(payload: never) => void | Promise<void>>>();\n\n const onEvent: EventBusContext[\"on\"] = ((\n eventName: EventName,\n handler: (payload: never) => void | Promise<void>\n ) => {\n let set = eventHandlers.get(eventName);\n if (!set) {\n set = new Set();\n eventHandlers.set(eventName, set);\n }\n set.add(handler);\n return () => {\n eventHandlers.get(eventName)?.delete(handler);\n };\n }) as EventBusContext[\"on\"];\n\n const emitEvent: EventBusContext[\"emit\"] = async (\n eventName: PluginEventName,\n payload: Record<string, unknown>\n ) => {\n emittedEvents.push({ eventName, payload });\n const handlers = eventHandlers.get(eventName);\n if (handlers) {\n for (const handler of handlers) {\n await handler(payload as never);\n }\n }\n };\n\n const events: EventBusContext = { on: onEvent, emit: emitEvent };\n\n // Build the fetch response queue\n const fetchQueue: MockFetchResponse[] = Array.isArray(opts.fetchResponse)\n ? [...opts.fetchResponse]\n : opts.fetchResponse\n ? [opts.fetchResponse]\n : [];\n\n const defaultFetchResponse: MockFetchResponse = { ok: true, status: 200, data: {} };\n\n const mockFetch: typeof fetch = (input, init) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n fetchCalls.push({ url, options: init });\n\n const response = fetchQueue.length > 0\n ? (fetchQueue.shift() ?? defaultFetchResponse)\n : defaultFetchResponse;\n\n const body = response.text ?? JSON.stringify(response.data ?? {});\n const status = response.status ?? (response.ok ? 200 : 400);\n\n return Promise.resolve(\n new Response(body, {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n })\n ) as ReturnType<typeof fetch>;\n };\n\n const tenant: ContextTenant = {\n id: opts.tenant?.id ?? \"test-tenant-id\",\n slug: opts.tenant?.slug ?? \"test-blog\",\n locale: opts.tenant?.locale ?? \"en-US\",\n };\n\n const settings: ContextSettings = {\n blogName: opts.settings?.blogName ?? \"Test Blog\",\n siteUrl: opts.settings?.siteUrl ?? \"https://test.velocms.org\",\n theme: opts.settings?.theme ?? \"default\",\n plan: opts.settings?.plan ?? \"pro\",\n };\n\n const ctx: MockHookContext = {\n tenant,\n settings,\n fetch: mockFetch,\n log: {\n info: (message, data) => logs.push({ level: \"info\", message, data }),\n warn: (message, data) => logs.push({ level: \"warn\", message, data }),\n error: (message, data) => logs.push({ level: \"error\", message, data }),\n },\n kv: {\n get: (key) => Promise.resolve(kvStore[key] ?? null),\n set: (key, value) => {\n kvStore[key] = value;\n return Promise.resolve();\n },\n delete: (key) => {\n delete kvStore[key];\n return Promise.resolve();\n },\n },\n events,\n _fetchCalls: fetchCalls,\n _logs: logs,\n _kvStore: kvStore,\n _emittedEvents: emittedEvents,\n _dispatchSystemEvent: async (eventName, payload) => {\n const handlers = eventHandlers.get(eventName);\n if (handlers) {\n for (const handler of handlers) {\n await handler(payload as never);\n }\n }\n },\n };\n\n return ctx;\n}\n\n/**\n * Reset a mock context's recorded calls, logs, and emitted events.\n * Useful for re-using a context across multiple test cases.\n *\n * Does NOT clear registered `ctx.events.on()` subscriptions — those persist\n * for the lifetime of the context, matching how a real plugin activation\n * (which typically registers subscriptions once) behaves.\n */\nexport function resetMockContext(ctx: MockHookContext): void {\n ctx._fetchCalls.length = 0;\n ctx._logs.length = 0;\n ctx._emittedEvents.length = 0;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,110 @@
1
+ import { u as PluginEventName, H as HookContext, S as SystemEventName, w as SystemEventPayloadMap, n as ContextTenant, m as ContextSettings } from './context-BCzNwlqR.cjs';
2
+
3
+ /**
4
+ * VeloCMS Plugin SDK — Test Helpers
5
+ *
6
+ * Provides mock implementations of the host bridge contract so plugin authors
7
+ * can write unit tests without a running VeloCMS instance.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { mockVelocms } from "@velocms/plugin-sdk/test-helpers";
12
+ *
13
+ * const ctx = mockVelocms({
14
+ * tenant: { id: "t1", slug: "my-blog", locale: "en-US" },
15
+ * fetchResponse: { ok: true, data: { result: "ok" } },
16
+ * });
17
+ *
18
+ * await myPlugin.handlePostPublish({ post: { slug: "hello" } }, ctx);
19
+ * expect(ctx._fetchCalls).toHaveLength(1);
20
+ * expect(ctx._fetchCalls[0].url).toBe("https://hooks.slack.com/...");
21
+ * ```
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+
26
+ interface MockFetchResponse {
27
+ ok: boolean;
28
+ status?: number;
29
+ /** JSON body returned by the mock */
30
+ data?: unknown;
31
+ text?: string;
32
+ }
33
+ interface MockVelocmsOptions {
34
+ tenant?: Partial<ContextTenant>;
35
+ settings?: Partial<ContextSettings>;
36
+ /**
37
+ * Static response for all fetch() calls.
38
+ * Pass an array to return different responses for successive calls.
39
+ */
40
+ fetchResponse?: MockFetchResponse | MockFetchResponse[];
41
+ /**
42
+ * Pre-populated key-value store entries.
43
+ */
44
+ kvStore?: Record<string, string>;
45
+ }
46
+ interface MockFetchCall {
47
+ url: string;
48
+ options?: RequestInit;
49
+ }
50
+ /** A custom event recorded when the plugin under test calls `ctx.events.emit()`. */
51
+ interface MockEmittedEvent {
52
+ eventName: PluginEventName;
53
+ payload: Record<string, unknown>;
54
+ }
55
+ interface MockHookContext extends HookContext {
56
+ /** All fetch() calls made during the test */
57
+ _fetchCalls: MockFetchCall[];
58
+ /** All log messages emitted during the test */
59
+ _logs: Array<{
60
+ level: "info" | "warn" | "error";
61
+ message: string;
62
+ data?: Record<string, unknown>;
63
+ }>;
64
+ /** Current key-value store state */
65
+ _kvStore: Record<string, string>;
66
+ /** All events emitted during the test via `ctx.events.emit()` */
67
+ _emittedEvents: MockEmittedEvent[];
68
+ /**
69
+ * Test-only helper: simulate VeloCMS core firing a system event, invoking
70
+ * every handler the plugin under test registered via `ctx.events.on(...)`.
71
+ *
72
+ * Not part of the real `HookContext` — the live event bus fires these
73
+ * automatically; in a unit test there's no running VeloCMS instance to
74
+ * fire them for you.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const ctx = mockVelocms();
79
+ * await myPlugin.onActivate(ctx); // registers ctx.events.on("post.published", ...)
80
+ * await ctx._dispatchSystemEvent("post.published", { post: { id: "1", title: "Hi" } });
81
+ * ```
82
+ */
83
+ _dispatchSystemEvent<E extends SystemEventName>(eventName: E, payload: SystemEventPayloadMap[E]): Promise<void>;
84
+ }
85
+ /**
86
+ * Create a mock `HookContext` for unit testing plugin hook handlers.
87
+ *
88
+ * The returned context is a fully-instrumented stand-in that:
89
+ * - Records all `ctx.fetch()` calls in `_fetchCalls`
90
+ * - Records all `ctx.log.*()` calls in `_logs`
91
+ * - Provides an in-memory `ctx.kv` store (seeded from `opts.kvStore`)
92
+ * - Returns controlled responses from `opts.fetchResponse`
93
+ *
94
+ * No network requests are made. No VeloCMS instance is required.
95
+ *
96
+ * @param opts - Optional overrides for tenant, settings, fetch responses, kv seed
97
+ * @returns - Instrumented `MockHookContext` compatible with `HookContext`
98
+ */
99
+ declare function mockVelocms(opts?: MockVelocmsOptions): MockHookContext;
100
+ /**
101
+ * Reset a mock context's recorded calls, logs, and emitted events.
102
+ * Useful for re-using a context across multiple test cases.
103
+ *
104
+ * Does NOT clear registered `ctx.events.on()` subscriptions — those persist
105
+ * for the lifetime of the context, matching how a real plugin activation
106
+ * (which typically registers subscriptions once) behaves.
107
+ */
108
+ declare function resetMockContext(ctx: MockHookContext): void;
109
+
110
+ export { type MockEmittedEvent, type MockFetchCall, type MockFetchResponse, type MockHookContext, type MockVelocmsOptions, mockVelocms, resetMockContext };
@@ -0,0 +1,110 @@
1
+ import { u as PluginEventName, H as HookContext, S as SystemEventName, w as SystemEventPayloadMap, n as ContextTenant, m as ContextSettings } from './context-BCzNwlqR.js';
2
+
3
+ /**
4
+ * VeloCMS Plugin SDK — Test Helpers
5
+ *
6
+ * Provides mock implementations of the host bridge contract so plugin authors
7
+ * can write unit tests without a running VeloCMS instance.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { mockVelocms } from "@velocms/plugin-sdk/test-helpers";
12
+ *
13
+ * const ctx = mockVelocms({
14
+ * tenant: { id: "t1", slug: "my-blog", locale: "en-US" },
15
+ * fetchResponse: { ok: true, data: { result: "ok" } },
16
+ * });
17
+ *
18
+ * await myPlugin.handlePostPublish({ post: { slug: "hello" } }, ctx);
19
+ * expect(ctx._fetchCalls).toHaveLength(1);
20
+ * expect(ctx._fetchCalls[0].url).toBe("https://hooks.slack.com/...");
21
+ * ```
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+
26
+ interface MockFetchResponse {
27
+ ok: boolean;
28
+ status?: number;
29
+ /** JSON body returned by the mock */
30
+ data?: unknown;
31
+ text?: string;
32
+ }
33
+ interface MockVelocmsOptions {
34
+ tenant?: Partial<ContextTenant>;
35
+ settings?: Partial<ContextSettings>;
36
+ /**
37
+ * Static response for all fetch() calls.
38
+ * Pass an array to return different responses for successive calls.
39
+ */
40
+ fetchResponse?: MockFetchResponse | MockFetchResponse[];
41
+ /**
42
+ * Pre-populated key-value store entries.
43
+ */
44
+ kvStore?: Record<string, string>;
45
+ }
46
+ interface MockFetchCall {
47
+ url: string;
48
+ options?: RequestInit;
49
+ }
50
+ /** A custom event recorded when the plugin under test calls `ctx.events.emit()`. */
51
+ interface MockEmittedEvent {
52
+ eventName: PluginEventName;
53
+ payload: Record<string, unknown>;
54
+ }
55
+ interface MockHookContext extends HookContext {
56
+ /** All fetch() calls made during the test */
57
+ _fetchCalls: MockFetchCall[];
58
+ /** All log messages emitted during the test */
59
+ _logs: Array<{
60
+ level: "info" | "warn" | "error";
61
+ message: string;
62
+ data?: Record<string, unknown>;
63
+ }>;
64
+ /** Current key-value store state */
65
+ _kvStore: Record<string, string>;
66
+ /** All events emitted during the test via `ctx.events.emit()` */
67
+ _emittedEvents: MockEmittedEvent[];
68
+ /**
69
+ * Test-only helper: simulate VeloCMS core firing a system event, invoking
70
+ * every handler the plugin under test registered via `ctx.events.on(...)`.
71
+ *
72
+ * Not part of the real `HookContext` — the live event bus fires these
73
+ * automatically; in a unit test there's no running VeloCMS instance to
74
+ * fire them for you.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const ctx = mockVelocms();
79
+ * await myPlugin.onActivate(ctx); // registers ctx.events.on("post.published", ...)
80
+ * await ctx._dispatchSystemEvent("post.published", { post: { id: "1", title: "Hi" } });
81
+ * ```
82
+ */
83
+ _dispatchSystemEvent<E extends SystemEventName>(eventName: E, payload: SystemEventPayloadMap[E]): Promise<void>;
84
+ }
85
+ /**
86
+ * Create a mock `HookContext` for unit testing plugin hook handlers.
87
+ *
88
+ * The returned context is a fully-instrumented stand-in that:
89
+ * - Records all `ctx.fetch()` calls in `_fetchCalls`
90
+ * - Records all `ctx.log.*()` calls in `_logs`
91
+ * - Provides an in-memory `ctx.kv` store (seeded from `opts.kvStore`)
92
+ * - Returns controlled responses from `opts.fetchResponse`
93
+ *
94
+ * No network requests are made. No VeloCMS instance is required.
95
+ *
96
+ * @param opts - Optional overrides for tenant, settings, fetch responses, kv seed
97
+ * @returns - Instrumented `MockHookContext` compatible with `HookContext`
98
+ */
99
+ declare function mockVelocms(opts?: MockVelocmsOptions): MockHookContext;
100
+ /**
101
+ * Reset a mock context's recorded calls, logs, and emitted events.
102
+ * Useful for re-using a context across multiple test cases.
103
+ *
104
+ * Does NOT clear registered `ctx.events.on()` subscriptions — those persist
105
+ * for the lifetime of the context, matching how a real plugin activation
106
+ * (which typically registers subscriptions once) behaves.
107
+ */
108
+ declare function resetMockContext(ctx: MockHookContext): void;
109
+
110
+ export { type MockEmittedEvent, type MockFetchCall, type MockFetchResponse, type MockHookContext, type MockVelocmsOptions, mockVelocms, resetMockContext };
@@ -0,0 +1,100 @@
1
+ // src/test-helpers.ts
2
+ function mockVelocms(opts = {}) {
3
+ const fetchCalls = [];
4
+ const logs = [];
5
+ const kvStore = { ...opts.kvStore ?? {} };
6
+ const emittedEvents = [];
7
+ const eventHandlers = /* @__PURE__ */ new Map();
8
+ const onEvent = ((eventName, handler) => {
9
+ let set = eventHandlers.get(eventName);
10
+ if (!set) {
11
+ set = /* @__PURE__ */ new Set();
12
+ eventHandlers.set(eventName, set);
13
+ }
14
+ set.add(handler);
15
+ return () => {
16
+ eventHandlers.get(eventName)?.delete(handler);
17
+ };
18
+ });
19
+ const emitEvent = async (eventName, payload) => {
20
+ emittedEvents.push({ eventName, payload });
21
+ const handlers = eventHandlers.get(eventName);
22
+ if (handlers) {
23
+ for (const handler of handlers) {
24
+ await handler(payload);
25
+ }
26
+ }
27
+ };
28
+ const events = { on: onEvent, emit: emitEvent };
29
+ const fetchQueue = Array.isArray(opts.fetchResponse) ? [...opts.fetchResponse] : opts.fetchResponse ? [opts.fetchResponse] : [];
30
+ const defaultFetchResponse = { ok: true, status: 200, data: {} };
31
+ const mockFetch = (input, init) => {
32
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
33
+ fetchCalls.push({ url, options: init });
34
+ const response = fetchQueue.length > 0 ? fetchQueue.shift() ?? defaultFetchResponse : defaultFetchResponse;
35
+ const body = response.text ?? JSON.stringify(response.data ?? {});
36
+ const status = response.status ?? (response.ok ? 200 : 400);
37
+ return Promise.resolve(
38
+ new Response(body, {
39
+ status,
40
+ headers: { "Content-Type": "application/json" }
41
+ })
42
+ );
43
+ };
44
+ const tenant = {
45
+ id: opts.tenant?.id ?? "test-tenant-id",
46
+ slug: opts.tenant?.slug ?? "test-blog",
47
+ locale: opts.tenant?.locale ?? "en-US"
48
+ };
49
+ const settings = {
50
+ blogName: opts.settings?.blogName ?? "Test Blog",
51
+ siteUrl: opts.settings?.siteUrl ?? "https://test.velocms.org",
52
+ theme: opts.settings?.theme ?? "default",
53
+ plan: opts.settings?.plan ?? "pro"
54
+ };
55
+ const ctx = {
56
+ tenant,
57
+ settings,
58
+ fetch: mockFetch,
59
+ log: {
60
+ info: (message, data) => logs.push({ level: "info", message, data }),
61
+ warn: (message, data) => logs.push({ level: "warn", message, data }),
62
+ error: (message, data) => logs.push({ level: "error", message, data })
63
+ },
64
+ kv: {
65
+ get: (key) => Promise.resolve(kvStore[key] ?? null),
66
+ set: (key, value) => {
67
+ kvStore[key] = value;
68
+ return Promise.resolve();
69
+ },
70
+ delete: (key) => {
71
+ delete kvStore[key];
72
+ return Promise.resolve();
73
+ }
74
+ },
75
+ events,
76
+ _fetchCalls: fetchCalls,
77
+ _logs: logs,
78
+ _kvStore: kvStore,
79
+ _emittedEvents: emittedEvents,
80
+ _dispatchSystemEvent: async (eventName, payload) => {
81
+ const handlers = eventHandlers.get(eventName);
82
+ if (handlers) {
83
+ for (const handler of handlers) {
84
+ await handler(payload);
85
+ }
86
+ }
87
+ }
88
+ };
89
+ return ctx;
90
+ }
91
+ function resetMockContext(ctx) {
92
+ ctx._fetchCalls.length = 0;
93
+ ctx._logs.length = 0;
94
+ ctx._emittedEvents.length = 0;
95
+ }
96
+ export {
97
+ mockVelocms,
98
+ resetMockContext
99
+ };
100
+ //# sourceMappingURL=test-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/test-helpers.ts"],"sourcesContent":["/**\n * VeloCMS Plugin SDK — Test Helpers\n *\n * Provides mock implementations of the host bridge contract so plugin authors\n * can write unit tests without a running VeloCMS instance.\n *\n * @example\n * ```typescript\n * import { mockVelocms } from \"@velocms/plugin-sdk/test-helpers\";\n *\n * const ctx = mockVelocms({\n * tenant: { id: \"t1\", slug: \"my-blog\", locale: \"en-US\" },\n * fetchResponse: { ok: true, data: { result: \"ok\" } },\n * });\n *\n * await myPlugin.handlePostPublish({ post: { slug: \"hello\" } }, ctx);\n * expect(ctx._fetchCalls).toHaveLength(1);\n * expect(ctx._fetchCalls[0].url).toBe(\"https://hooks.slack.com/...\");\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { HookContext, ContextTenant, ContextSettings } from \"./types/context\";\nimport type {\n EventBusContext,\n EventName,\n SystemEventName,\n PluginEventName,\n SystemEventPayloadMap,\n} from \"./types/events\";\n\n// ---------------------------------------------------------------------------\n// Mock configuration types\n// ---------------------------------------------------------------------------\n\nexport interface MockFetchResponse {\n ok: boolean;\n status?: number;\n /** JSON body returned by the mock */\n data?: unknown;\n text?: string;\n}\n\nexport interface MockVelocmsOptions {\n tenant?: Partial<ContextTenant>;\n settings?: Partial<ContextSettings>;\n /**\n * Static response for all fetch() calls.\n * Pass an array to return different responses for successive calls.\n */\n fetchResponse?: MockFetchResponse | MockFetchResponse[];\n /**\n * Pre-populated key-value store entries.\n */\n kvStore?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Instrumented context (extends HookContext with test introspection)\n// ---------------------------------------------------------------------------\n\nexport interface MockFetchCall {\n url: string;\n options?: RequestInit;\n}\n\n/** A custom event recorded when the plugin under test calls `ctx.events.emit()`. */\nexport interface MockEmittedEvent {\n eventName: PluginEventName;\n payload: Record<string, unknown>;\n}\n\nexport interface MockHookContext extends HookContext {\n /** All fetch() calls made during the test */\n _fetchCalls: MockFetchCall[];\n /** All log messages emitted during the test */\n _logs: Array<{ level: \"info\" | \"warn\" | \"error\"; message: string; data?: Record<string, unknown> }>;\n /** Current key-value store state */\n _kvStore: Record<string, string>;\n /** All events emitted during the test via `ctx.events.emit()` */\n _emittedEvents: MockEmittedEvent[];\n /**\n * Test-only helper: simulate VeloCMS core firing a system event, invoking\n * every handler the plugin under test registered via `ctx.events.on(...)`.\n *\n * Not part of the real `HookContext` — the live event bus fires these\n * automatically; in a unit test there's no running VeloCMS instance to\n * fire them for you.\n *\n * @example\n * ```typescript\n * const ctx = mockVelocms();\n * await myPlugin.onActivate(ctx); // registers ctx.events.on(\"post.published\", ...)\n * await ctx._dispatchSystemEvent(\"post.published\", { post: { id: \"1\", title: \"Hi\" } });\n * ```\n */\n _dispatchSystemEvent<E extends SystemEventName>(\n eventName: E,\n payload: SystemEventPayloadMap[E]\n ): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a mock `HookContext` for unit testing plugin hook handlers.\n *\n * The returned context is a fully-instrumented stand-in that:\n * - Records all `ctx.fetch()` calls in `_fetchCalls`\n * - Records all `ctx.log.*()` calls in `_logs`\n * - Provides an in-memory `ctx.kv` store (seeded from `opts.kvStore`)\n * - Returns controlled responses from `opts.fetchResponse`\n *\n * No network requests are made. No VeloCMS instance is required.\n *\n * @param opts - Optional overrides for tenant, settings, fetch responses, kv seed\n * @returns - Instrumented `MockHookContext` compatible with `HookContext`\n */\nexport function mockVelocms(opts: MockVelocmsOptions = {}): MockHookContext {\n const fetchCalls: MockFetchCall[] = [];\n const logs: MockHookContext[\"_logs\"] = [];\n const kvStore: Record<string, string> = { ...(opts.kvStore ?? {}) };\n const emittedEvents: MockEmittedEvent[] = [];\n\n // In-memory pub/sub for ctx.events — mirrors the real EventBusContext\n // shape without a running VeloCMS instance.\n const eventHandlers = new Map<EventName, Set<(payload: never) => void | Promise<void>>>();\n\n const onEvent: EventBusContext[\"on\"] = ((\n eventName: EventName,\n handler: (payload: never) => void | Promise<void>\n ) => {\n let set = eventHandlers.get(eventName);\n if (!set) {\n set = new Set();\n eventHandlers.set(eventName, set);\n }\n set.add(handler);\n return () => {\n eventHandlers.get(eventName)?.delete(handler);\n };\n }) as EventBusContext[\"on\"];\n\n const emitEvent: EventBusContext[\"emit\"] = async (\n eventName: PluginEventName,\n payload: Record<string, unknown>\n ) => {\n emittedEvents.push({ eventName, payload });\n const handlers = eventHandlers.get(eventName);\n if (handlers) {\n for (const handler of handlers) {\n await handler(payload as never);\n }\n }\n };\n\n const events: EventBusContext = { on: onEvent, emit: emitEvent };\n\n // Build the fetch response queue\n const fetchQueue: MockFetchResponse[] = Array.isArray(opts.fetchResponse)\n ? [...opts.fetchResponse]\n : opts.fetchResponse\n ? [opts.fetchResponse]\n : [];\n\n const defaultFetchResponse: MockFetchResponse = { ok: true, status: 200, data: {} };\n\n const mockFetch: typeof fetch = (input, init) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n fetchCalls.push({ url, options: init });\n\n const response = fetchQueue.length > 0\n ? (fetchQueue.shift() ?? defaultFetchResponse)\n : defaultFetchResponse;\n\n const body = response.text ?? JSON.stringify(response.data ?? {});\n const status = response.status ?? (response.ok ? 200 : 400);\n\n return Promise.resolve(\n new Response(body, {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n })\n ) as ReturnType<typeof fetch>;\n };\n\n const tenant: ContextTenant = {\n id: opts.tenant?.id ?? \"test-tenant-id\",\n slug: opts.tenant?.slug ?? \"test-blog\",\n locale: opts.tenant?.locale ?? \"en-US\",\n };\n\n const settings: ContextSettings = {\n blogName: opts.settings?.blogName ?? \"Test Blog\",\n siteUrl: opts.settings?.siteUrl ?? \"https://test.velocms.org\",\n theme: opts.settings?.theme ?? \"default\",\n plan: opts.settings?.plan ?? \"pro\",\n };\n\n const ctx: MockHookContext = {\n tenant,\n settings,\n fetch: mockFetch,\n log: {\n info: (message, data) => logs.push({ level: \"info\", message, data }),\n warn: (message, data) => logs.push({ level: \"warn\", message, data }),\n error: (message, data) => logs.push({ level: \"error\", message, data }),\n },\n kv: {\n get: (key) => Promise.resolve(kvStore[key] ?? null),\n set: (key, value) => {\n kvStore[key] = value;\n return Promise.resolve();\n },\n delete: (key) => {\n delete kvStore[key];\n return Promise.resolve();\n },\n },\n events,\n _fetchCalls: fetchCalls,\n _logs: logs,\n _kvStore: kvStore,\n _emittedEvents: emittedEvents,\n _dispatchSystemEvent: async (eventName, payload) => {\n const handlers = eventHandlers.get(eventName);\n if (handlers) {\n for (const handler of handlers) {\n await handler(payload as never);\n }\n }\n },\n };\n\n return ctx;\n}\n\n/**\n * Reset a mock context's recorded calls, logs, and emitted events.\n * Useful for re-using a context across multiple test cases.\n *\n * Does NOT clear registered `ctx.events.on()` subscriptions — those persist\n * for the lifetime of the context, matching how a real plugin activation\n * (which typically registers subscriptions once) behaves.\n */\nexport function resetMockContext(ctx: MockHookContext): void {\n ctx._fetchCalls.length = 0;\n ctx._logs.length = 0;\n ctx._emittedEvents.length = 0;\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@velocms/plugin-sdk",
3
+ "version": "1.0.0-alpha.2",
4
+ "description": "Plugin SDK for VeloCMS — types, manifests, lifecycle hooks for first-party + community plugins",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./test-helpers": {
16
+ "types": "./dist/test-helpers.d.ts",
17
+ "import": "./dist/test-helpers.js",
18
+ "require": "./dist/test-helpers.cjs"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist/**",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "typecheck": "tsc --noEmit",
29
+ "test": "vitest run",
30
+ "prepublishOnly": "npm run build && npm run typecheck"
31
+ },
32
+ "keywords": [
33
+ "velocms",
34
+ "cms",
35
+ "sdk",
36
+ "plugin-sdk",
37
+ "blog",
38
+ "nextjs",
39
+ "typescript",
40
+ "plugin",
41
+ "marketplace",
42
+ "hooks"
43
+ ],
44
+ "license": "MIT",
45
+ "author": {
46
+ "name": "VeloCMS",
47
+ "url": "https://velocms.org"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/VeloCMS/plugin-sdk.git"
52
+ },
53
+ "homepage": "https://velocms.org/developers/sdk",
54
+ "bugs": {
55
+ "url": "https://github.com/VeloCMS/plugin-sdk/issues"
56
+ },
57
+ "peerDependencies": {
58
+ "typescript": ">=5.0.0"
59
+ },
60
+ "devDependencies": {
61
+ "tsup": "^8.0.0",
62
+ "typescript": "^5.0.0",
63
+ "vitest": "^4.0.0"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public",
67
+ "registry": "https://registry.npmjs.org/"
68
+ },
69
+ "sideEffects": false
70
+ }