@actuate-media/cms-core 0.21.0 → 0.22.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.
Files changed (105) hide show
  1. package/dist/__tests__/actions/document-crud.test.js +74 -2
  2. package/dist/__tests__/actions/document-crud.test.js.map +1 -1
  3. package/dist/__tests__/api/cache-headers.test.d.ts +2 -0
  4. package/dist/__tests__/api/cache-headers.test.d.ts.map +1 -0
  5. package/dist/__tests__/api/cache-headers.test.js +135 -0
  6. package/dist/__tests__/api/cache-headers.test.js.map +1 -0
  7. package/dist/__tests__/api/comments-routes.test.d.ts +2 -0
  8. package/dist/__tests__/api/comments-routes.test.d.ts.map +1 -0
  9. package/dist/__tests__/api/comments-routes.test.js +266 -0
  10. package/dist/__tests__/api/comments-routes.test.js.map +1 -0
  11. package/dist/__tests__/api/notifications-routes.test.d.ts +2 -0
  12. package/dist/__tests__/api/notifications-routes.test.d.ts.map +1 -0
  13. package/dist/__tests__/api/notifications-routes.test.js +225 -0
  14. package/dist/__tests__/api/notifications-routes.test.js.map +1 -0
  15. package/dist/__tests__/api/preview-and-scheduling.test.js +53 -0
  16. package/dist/__tests__/api/preview-and-scheduling.test.js.map +1 -1
  17. package/dist/__tests__/fields/relations.test.js +62 -8
  18. package/dist/__tests__/fields/relations.test.js.map +1 -1
  19. package/dist/__tests__/preview-event-bus.test.d.ts +2 -0
  20. package/dist/__tests__/preview-event-bus.test.d.ts.map +1 -0
  21. package/dist/__tests__/preview-event-bus.test.js +89 -0
  22. package/dist/__tests__/preview-event-bus.test.js.map +1 -0
  23. package/dist/__tests__/preview-sse-stream.test.d.ts +2 -0
  24. package/dist/__tests__/preview-sse-stream.test.d.ts.map +1 -0
  25. package/dist/__tests__/preview-sse-stream.test.js +117 -0
  26. package/dist/__tests__/preview-sse-stream.test.js.map +1 -0
  27. package/dist/__tests__/realtime/comments.test.d.ts +2 -0
  28. package/dist/__tests__/realtime/comments.test.d.ts.map +1 -0
  29. package/dist/__tests__/realtime/comments.test.js +508 -0
  30. package/dist/__tests__/realtime/comments.test.js.map +1 -0
  31. package/dist/__tests__/realtime/gateway-ws-integration.test.d.ts +2 -0
  32. package/dist/__tests__/realtime/gateway-ws-integration.test.d.ts.map +1 -0
  33. package/dist/__tests__/realtime/gateway-ws-integration.test.js +170 -0
  34. package/dist/__tests__/realtime/gateway-ws-integration.test.js.map +1 -0
  35. package/dist/__tests__/realtime/gateway.test.d.ts +2 -0
  36. package/dist/__tests__/realtime/gateway.test.d.ts.map +1 -0
  37. package/dist/__tests__/realtime/gateway.test.js +402 -0
  38. package/dist/__tests__/realtime/gateway.test.js.map +1 -0
  39. package/dist/__tests__/realtime/notifications.test.d.ts +2 -0
  40. package/dist/__tests__/realtime/notifications.test.d.ts.map +1 -0
  41. package/dist/__tests__/realtime/notifications.test.js +317 -0
  42. package/dist/__tests__/realtime/notifications.test.js.map +1 -0
  43. package/dist/__tests__/realtime/persistence.test.d.ts +2 -0
  44. package/dist/__tests__/realtime/persistence.test.d.ts.map +1 -0
  45. package/dist/__tests__/realtime/persistence.test.js +124 -0
  46. package/dist/__tests__/realtime/persistence.test.js.map +1 -0
  47. package/dist/__tests__/schema-preview-features.test.d.ts +2 -0
  48. package/dist/__tests__/schema-preview-features.test.d.ts.map +1 -0
  49. package/dist/__tests__/schema-preview-features.test.js +42 -0
  50. package/dist/__tests__/schema-preview-features.test.js.map +1 -0
  51. package/dist/actions.d.ts.map +1 -1
  52. package/dist/actions.js +71 -26
  53. package/dist/actions.js.map +1 -1
  54. package/dist/api/handlers.d.ts.map +1 -1
  55. package/dist/api/handlers.js +734 -188
  56. package/dist/api/handlers.js.map +1 -1
  57. package/dist/cache/__tests__/http.test.d.ts +2 -0
  58. package/dist/cache/__tests__/http.test.d.ts.map +1 -0
  59. package/dist/cache/__tests__/http.test.js +285 -0
  60. package/dist/cache/__tests__/http.test.js.map +1 -0
  61. package/dist/cache/http.d.ts +119 -0
  62. package/dist/cache/http.d.ts.map +1 -0
  63. package/dist/cache/http.js +188 -0
  64. package/dist/cache/http.js.map +1 -0
  65. package/dist/config/types.d.ts +92 -0
  66. package/dist/config/types.d.ts.map +1 -1
  67. package/dist/fields/relations.d.ts.map +1 -1
  68. package/dist/fields/relations.js +43 -11
  69. package/dist/fields/relations.js.map +1 -1
  70. package/dist/index.d.ts +5 -0
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +2 -0
  73. package/dist/index.js.map +1 -1
  74. package/dist/page-builder/schema.d.ts +2 -2
  75. package/dist/preview/event-bus.d.ts +39 -0
  76. package/dist/preview/event-bus.d.ts.map +1 -0
  77. package/dist/preview/event-bus.js +73 -0
  78. package/dist/preview/event-bus.js.map +1 -0
  79. package/dist/preview/sse-stream.d.ts +50 -0
  80. package/dist/preview/sse-stream.d.ts.map +1 -0
  81. package/dist/preview/sse-stream.js +120 -0
  82. package/dist/preview/sse-stream.js.map +1 -0
  83. package/dist/realtime/comments.d.ts +217 -0
  84. package/dist/realtime/comments.d.ts.map +1 -0
  85. package/dist/realtime/comments.js +282 -0
  86. package/dist/realtime/comments.js.map +1 -0
  87. package/dist/realtime/gateway.d.ts +107 -0
  88. package/dist/realtime/gateway.d.ts.map +1 -0
  89. package/dist/realtime/gateway.js +215 -0
  90. package/dist/realtime/gateway.js.map +1 -0
  91. package/dist/realtime/index.d.ts +18 -0
  92. package/dist/realtime/index.d.ts.map +1 -0
  93. package/dist/realtime/index.js +18 -0
  94. package/dist/realtime/index.js.map +1 -0
  95. package/dist/realtime/notifications.d.ts +209 -0
  96. package/dist/realtime/notifications.d.ts.map +1 -0
  97. package/dist/realtime/notifications.js +209 -0
  98. package/dist/realtime/notifications.js.map +1 -0
  99. package/dist/realtime/persistence.d.ts +72 -0
  100. package/dist/realtime/persistence.d.ts.map +1 -0
  101. package/dist/realtime/persistence.js +53 -0
  102. package/dist/realtime/persistence.js.map +1 -0
  103. package/package.json +13 -4
  104. package/prisma/cms-schema.prisma +135 -4
  105. package/prisma/schema.prisma +17 -3
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { __resetPreviewEventBus, notifyPreviewUpdate, previewSubscriberCount, subscribePreviewUpdates, } from '../preview/event-bus.js';
3
+ beforeEach(() => {
4
+ __resetPreviewEventBus();
5
+ });
6
+ describe('previewSubscriberCount', () => {
7
+ it('returns 0 for a key with no subscribers', () => {
8
+ expect(previewSubscriberCount('posts', 'doc-1')).toBe(0);
9
+ });
10
+ it('returns the number of subscribers for that doc', () => {
11
+ subscribePreviewUpdates('posts', 'doc-1', () => undefined);
12
+ subscribePreviewUpdates('posts', 'doc-1', () => undefined);
13
+ subscribePreviewUpdates('posts', 'doc-2', () => undefined);
14
+ expect(previewSubscriberCount('posts', 'doc-1')).toBe(2);
15
+ expect(previewSubscriberCount('posts', 'doc-2')).toBe(1);
16
+ });
17
+ });
18
+ describe('subscribePreviewUpdates + notifyPreviewUpdate', () => {
19
+ it('delivers a payload to a subscriber', () => {
20
+ const listener = vi.fn();
21
+ subscribePreviewUpdates('posts', 'doc-1', listener);
22
+ notifyPreviewUpdate('posts', 'doc-1', { title: 'Hello' });
23
+ expect(listener).toHaveBeenCalledTimes(1);
24
+ expect(listener.mock.calls[0][0]).toMatchObject({
25
+ collection: 'posts',
26
+ documentId: 'doc-1',
27
+ payload: { title: 'Hello' },
28
+ });
29
+ expect(listener.mock.calls[0][0].at).toBeTypeOf('number');
30
+ });
31
+ it('delivers to every subscriber for the same doc', () => {
32
+ const a = vi.fn();
33
+ const b = vi.fn();
34
+ subscribePreviewUpdates('posts', 'doc-1', a);
35
+ subscribePreviewUpdates('posts', 'doc-1', b);
36
+ notifyPreviewUpdate('posts', 'doc-1', { v: 1 });
37
+ expect(a).toHaveBeenCalledTimes(1);
38
+ expect(b).toHaveBeenCalledTimes(1);
39
+ });
40
+ it('does not deliver to subscribers on a different doc', () => {
41
+ const a = vi.fn();
42
+ const b = vi.fn();
43
+ subscribePreviewUpdates('posts', 'doc-1', a);
44
+ subscribePreviewUpdates('posts', 'doc-2', b);
45
+ notifyPreviewUpdate('posts', 'doc-1', { v: 1 });
46
+ expect(a).toHaveBeenCalledTimes(1);
47
+ expect(b).not.toHaveBeenCalled();
48
+ });
49
+ it('does not deliver across collections', () => {
50
+ const a = vi.fn();
51
+ const b = vi.fn();
52
+ subscribePreviewUpdates('posts', 'doc-1', a);
53
+ subscribePreviewUpdates('pages', 'doc-1', b);
54
+ notifyPreviewUpdate('posts', 'doc-1', { v: 1 });
55
+ expect(a).toHaveBeenCalledTimes(1);
56
+ expect(b).not.toHaveBeenCalled();
57
+ });
58
+ it('removes a subscriber via the returned unsubscribe handle', () => {
59
+ const listener = vi.fn();
60
+ const unsubscribe = subscribePreviewUpdates('posts', 'doc-1', listener);
61
+ notifyPreviewUpdate('posts', 'doc-1', { v: 1 });
62
+ unsubscribe();
63
+ notifyPreviewUpdate('posts', 'doc-1', { v: 2 });
64
+ expect(listener).toHaveBeenCalledTimes(1);
65
+ expect(previewSubscriberCount('posts', 'doc-1')).toBe(0);
66
+ });
67
+ it('isolates throwing listeners — siblings still get called', () => {
68
+ const error = new Error('boom');
69
+ const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined);
70
+ const ok = vi.fn();
71
+ subscribePreviewUpdates('posts', 'doc-1', () => {
72
+ throw error;
73
+ });
74
+ subscribePreviewUpdates('posts', 'doc-1', ok);
75
+ expect(() => notifyPreviewUpdate('posts', 'doc-1', { v: 1 })).not.toThrow();
76
+ expect(ok).toHaveBeenCalledTimes(1);
77
+ consoleError.mockRestore();
78
+ });
79
+ it('skips notify when there are no subscribers', () => {
80
+ expect(() => notifyPreviewUpdate('posts', 'doc-1', { v: 1 })).not.toThrow();
81
+ });
82
+ it('is safe to unsubscribe twice', () => {
83
+ const unsubscribe = subscribePreviewUpdates('posts', 'doc-1', () => undefined);
84
+ unsubscribe();
85
+ expect(() => unsubscribe()).not.toThrow();
86
+ expect(previewSubscriberCount('posts', 'doc-1')).toBe(0);
87
+ });
88
+ });
89
+ //# sourceMappingURL=preview-event-bus.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-event-bus.test.js","sourceRoot":"","sources":["../../src/__tests__/preview-event-bus.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=preview-sse-stream.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-sse-stream.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/preview-sse-stream.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,117 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { createPreviewStream, createPreviewStreamResponse, encodeSseEvent, PREVIEW_SSE_HEADERS, } from '../preview/sse-stream.js';
3
+ import { __resetPreviewEventBus, notifyPreviewUpdate } from '../preview/event-bus.js';
4
+ beforeEach(() => {
5
+ __resetPreviewEventBus();
6
+ });
7
+ const decoder = new TextDecoder();
8
+ async function readNext(reader, timeoutMs = 1000) {
9
+ let timer;
10
+ const timeout = new Promise((_, reject) => {
11
+ timer = setTimeout(() => reject(new Error(`SSE read timed out after ${timeoutMs}ms`)), timeoutMs);
12
+ });
13
+ try {
14
+ const result = await Promise.race([reader.read(), timeout]);
15
+ if (result.done)
16
+ throw new Error('Stream closed before a chunk arrived');
17
+ return decoder.decode(result.value);
18
+ }
19
+ finally {
20
+ if (timer)
21
+ clearTimeout(timer);
22
+ }
23
+ }
24
+ describe('encodeSseEvent', () => {
25
+ it('serializes default (anonymous) messages', () => {
26
+ expect(encodeSseEvent({ hello: 'world' })).toBe('data: {"hello":"world"}\n\n');
27
+ });
28
+ it('emits an event tag when provided', () => {
29
+ expect(encodeSseEvent({ ok: true }, 'update')).toBe('event: update\ndata: {"ok":true}\n\n');
30
+ });
31
+ it('splits multi-line strings into multiple data lines', () => {
32
+ expect(encodeSseEvent('line one\nline two')).toBe('data: line one\ndata: line two\n\n');
33
+ });
34
+ it('passes strings through without re-stringifying', () => {
35
+ expect(encodeSseEvent('hello')).toBe('data: hello\n\n');
36
+ });
37
+ });
38
+ describe('createPreviewStream', () => {
39
+ let reader;
40
+ afterEach(async () => {
41
+ if (reader) {
42
+ await reader.cancel().catch(() => undefined);
43
+ reader = undefined;
44
+ }
45
+ });
46
+ it('sends a `ready` event on connection', async () => {
47
+ const stream = createPreviewStream({ collection: 'posts', documentId: 'doc-1' }, { heartbeatMs: 60_000, maxLifetimeMs: 60_000 });
48
+ reader = stream.getReader();
49
+ const chunk = await readNext(reader);
50
+ expect(chunk).toContain('event: ready');
51
+ expect(chunk).toContain('"collection":"posts"');
52
+ expect(chunk).toContain('"documentId":"doc-1"');
53
+ });
54
+ it('forwards `notifyPreviewUpdate` payloads as `update` events', async () => {
55
+ const stream = createPreviewStream({ collection: 'posts', documentId: 'doc-1' }, { heartbeatMs: 60_000, maxLifetimeMs: 60_000 });
56
+ reader = stream.getReader();
57
+ await readNext(reader); // discard the ready handshake
58
+ notifyPreviewUpdate('posts', 'doc-1', { values: { title: 'New' } });
59
+ const chunk = await readNext(reader);
60
+ expect(chunk).toContain('event: update');
61
+ expect(chunk).toContain('"values":{"title":"New"}');
62
+ });
63
+ it('ignores notifications for other documents', async () => {
64
+ const stream = createPreviewStream({ collection: 'posts', documentId: 'doc-1' }, { heartbeatMs: 60_000, maxLifetimeMs: 60_000 });
65
+ reader = stream.getReader();
66
+ await readNext(reader); // ready
67
+ notifyPreviewUpdate('posts', 'doc-2', { values: { title: 'Other doc' } });
68
+ notifyPreviewUpdate('posts', 'doc-1', { values: { title: 'Our doc' } });
69
+ const chunk = await readNext(reader);
70
+ expect(chunk).toContain('"title":"Our doc"');
71
+ expect(chunk).not.toContain('"title":"Other doc"');
72
+ });
73
+ it('sends heartbeat comments on the heartbeat interval', async () => {
74
+ const stream = createPreviewStream({ collection: 'posts', documentId: 'doc-1' }, { heartbeatMs: 10, maxLifetimeMs: 60_000 });
75
+ reader = stream.getReader();
76
+ await readNext(reader); // ready
77
+ const chunk = await readNext(reader, 500);
78
+ expect(chunk).toContain(': heartbeat');
79
+ });
80
+ it('closes after `maxLifetimeMs` so EventSource can reconnect', async () => {
81
+ const stream = createPreviewStream({ collection: 'posts', documentId: 'doc-1' }, { heartbeatMs: 60_000, maxLifetimeMs: 25 });
82
+ reader = stream.getReader();
83
+ await readNext(reader); // ready
84
+ // Drain pending chunks until the stream closes naturally.
85
+ const start = Date.now();
86
+ let closed = false;
87
+ while (Date.now() - start < 1000) {
88
+ const result = await reader.read();
89
+ if (result.done) {
90
+ closed = true;
91
+ break;
92
+ }
93
+ }
94
+ expect(closed).toBe(true);
95
+ });
96
+ it('removes the subscription on `cancel`', async () => {
97
+ const { previewSubscriberCount } = await import('../preview/event-bus.js');
98
+ const stream = createPreviewStream({ collection: 'posts', documentId: 'doc-1' }, { heartbeatMs: 60_000, maxLifetimeMs: 60_000 });
99
+ reader = stream.getReader();
100
+ await readNext(reader); // ready — guarantees `start` ran
101
+ expect(previewSubscriberCount('posts', 'doc-1')).toBe(1);
102
+ await reader.cancel();
103
+ reader = undefined;
104
+ expect(previewSubscriberCount('posts', 'doc-1')).toBe(0);
105
+ });
106
+ });
107
+ describe('createPreviewStreamResponse', () => {
108
+ it('returns a Response with SSE headers', async () => {
109
+ const res = createPreviewStreamResponse({ collection: 'posts', documentId: 'doc-1' }, { heartbeatMs: 60_000, maxLifetimeMs: 60_000 });
110
+ expect(res.status).toBe(200);
111
+ for (const [key, value] of Object.entries(PREVIEW_SSE_HEADERS)) {
112
+ expect(res.headers.get(key)).toBe(value);
113
+ }
114
+ await res.body?.cancel().catch(() => undefined);
115
+ });
116
+ });
117
+ //# sourceMappingURL=preview-sse-stream.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-sse-stream.test.js","sourceRoot":"","sources":["../../src/__tests__/preview-sse-stream.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=comments.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/realtime/comments.test.ts"],"names":[],"mappings":""}