@manablox/api-public 0.1.0 → 0.3.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.
@@ -1,177 +0,0 @@
1
- import { Manablox } from '@manablox/core';
2
- import { builtinFieldTypes } from '@manablox/fields';
3
- import { beforeAll, describe, expect, it } from 'vitest';
4
- import type { PublicContext } from '../src/context.js';
5
- import { parseExpand, serializeContents } from '../src/serialize.js';
6
-
7
- const SPACE = '11111111-1111-4111-8111-111111111111';
8
- const ASSET = '22222222-2222-4222-8222-222222222222';
9
- const RELATED = '33333333-3333-4333-8333-333333333333';
10
-
11
- let manablox: Manablox;
12
- let loads: string[];
13
-
14
- beforeAll(async () => {
15
- manablox = new Manablox({
16
- database: { url: 'postgres://unused' },
17
- auth: { secret: 'test' },
18
- fieldTypes: builtinFieldTypes,
19
- logLevel: 'silent',
20
- contentTypes: [
21
- {
22
- name: 'teaser',
23
- kind: 'block',
24
- fields: [
25
- { name: 'headline', type: 'string' },
26
- { name: 'image', type: 'asset' },
27
- ],
28
- },
29
- {
30
- name: 'page',
31
- fields: [
32
- { name: 'summary', type: 'string' },
33
- { name: 'hero', type: 'asset' },
34
- { name: 'related', type: 'content', settings: { multiple: true, types: [] } },
35
- { name: 'secret', type: 'string', readRoles: ['admin'] },
36
- ],
37
- },
38
- ],
39
- });
40
- await manablox.init();
41
- });
42
-
43
- const asset = (id: string) => ({
44
- id,
45
- spaceId: SPACE,
46
- filename: 'x.jpg',
47
- name: 'x',
48
- mimeType: 'image/jpeg',
49
- size: 1,
50
- width: 10,
51
- height: 10,
52
- alt: 'alt',
53
- title: null,
54
- });
55
-
56
- function context(): PublicContext {
57
- loads = [];
58
- const loader = <T>(build: (id: string) => T | null, label: string) => ({
59
- load: async (id: string) => {
60
- loads.push(`${label}:1`);
61
- return build(id);
62
- },
63
- loadMany: async (ids: string[]) => {
64
- // One entry per *batch*, so the test can assert batching rather than call count.
65
- loads.push(`${label}:${ids.length}`);
66
- return ids.map(build);
67
- },
68
- });
69
-
70
- return {
71
- manablox,
72
- repos: {} as never,
73
- media: { urlFor: (row: { id: string }) => `https://cdn.test/${row.id}` } as never,
74
- loaders: {
75
- asset: loader((id) => asset(id), 'asset'),
76
- user: loader(() => null, 'user'),
77
- publishedContent: loader(
78
- (id) => ({
79
- id,
80
- typeId: manablox.contentTypes.getByName('page').id,
81
- title: 'Related',
82
- permalink: 'related',
83
- locale: 'en',
84
- }),
85
- 'content',
86
- ),
87
- content: loader(() => null, 'draft'),
88
- children: loader(() => [], 'children'),
89
- } as never,
90
- spaceId: SPACE,
91
- locale: 'en',
92
- };
93
- }
94
-
95
- const row = (over: Record<string, unknown> = {}) =>
96
- ({
97
- id: 'c1',
98
- spaceId: SPACE,
99
- typeId: manablox.contentTypes.getByName('page').id,
100
- title: 'A page',
101
- slug: 'a-page',
102
- permalink: 'a-page',
103
- locale: 'en',
104
- parentId: null,
105
- publishedAt: new Date('2026-01-01T00:00:00Z'),
106
- updatedAt: new Date('2026-01-02T00:00:00Z'),
107
- fields: { summary: 'hello', hero: ASSET, related: [RELATED], secret: 'nope' },
108
- ...over,
109
- }) as never;
110
-
111
- describe('serializer', () => {
112
- it('returns fields as a plain map keyed by field name', async () => {
113
- const [result] = await serializeContents([row()], context(), parseExpand(undefined));
114
- expect(result?.fields).toEqual({ summary: 'hello', hero: ASSET, related: [RELATED] });
115
- expect(result?.type).toBe('page');
116
- expect(result?.publishedAt).toBe('2026-01-01T00:00:00.000Z');
117
- });
118
-
119
- it('omits role-gated fields', async () => {
120
- const [result] = await serializeContents([row()], context(), parseExpand(undefined));
121
- expect(result?.fields).not.toHaveProperty('secret');
122
- });
123
-
124
- it('inlines only the relations named in expand', async () => {
125
- const [result] = await serializeContents([row()], context(), parseExpand('hero'));
126
- expect(result?.fields.hero).toMatchObject({ id: ASSET, url: `https://cdn.test/${ASSET}` });
127
- // `related` was not requested, so it stays a list of ids.
128
- expect(result?.fields.related).toEqual([RELATED]);
129
- });
130
-
131
- it('loads each relation target in one batch however many rows there are', async () => {
132
- const rows = Array.from({ length: 50 }, (_, i) => row({ id: `c${i}` }));
133
- await serializeContents(rows, context(), parseExpand('hero,related'));
134
-
135
- // One asset batch and one content batch — not fifty of each.
136
- expect(loads.filter((entry) => entry.startsWith('asset:'))).toEqual(['asset:1']);
137
- expect(loads.filter((entry) => entry.startsWith('content:'))).toEqual(['content:1']);
138
- });
139
-
140
- it('walks into blocks', async () => {
141
- const teaser = manablox.contentTypes.getByName('teaser');
142
- const withBlocks = row({
143
- typeId: manablox.contentTypes.getByName('page').id,
144
- fields: {
145
- summary: 's',
146
- hero: null,
147
- related: [],
148
- blocks: [{ blockId: 'b1', type: teaser.id, fields: { headline: 'Hi', image: ASSET } }],
149
- },
150
- });
151
-
152
- // `page` has no blocks field, so nothing should appear — the walker must not invent
153
- // fields the content type does not declare.
154
- const [result] = await serializeContents([withBlocks], context(), parseExpand(undefined));
155
- expect(result?.fields).not.toHaveProperty('blocks');
156
- });
157
-
158
- it('inlines a content relation as a summary, not a recursive expansion', async () => {
159
- const [result] = await serializeContents([row()], context(), parseExpand('related'));
160
- const [related] = result?.fields.related as Array<Record<string, unknown>>;
161
- expect(related).toEqual({
162
- id: RELATED,
163
- type: 'page',
164
- title: 'Related',
165
- permalink: 'related',
166
- locale: 'en',
167
- });
168
- expect(related).not.toHaveProperty('fields');
169
- });
170
- });
171
-
172
- describe('parseExpand', () => {
173
- it('accepts a comma list and ignores blanks', () => {
174
- expect([...parseExpand('hero, author,,')]).toEqual(['hero', 'author']);
175
- expect(parseExpand(undefined).size).toBe(0);
176
- });
177
- });
package/tsconfig.json DELETED
@@ -1 +0,0 @@
1
- { "extends": "@manablox/config-typescript/library.json", "include": ["src", "test"] }
package/vitest.config.ts DELETED
@@ -1,5 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: { environment: 'node', include: ['test/**/*.test.ts'] },
5
- });