@sequoialabs/payload-plugin-reversia 0.1.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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/dist/collections/sync-pending.d.ts +2 -0
  4. package/dist/collections/sync-pending.js +27 -0
  5. package/dist/endpoints/confirm-resources-sync.d.ts +3 -0
  6. package/dist/endpoints/confirm-resources-sync.js +31 -0
  7. package/dist/endpoints/resource.d.ts +3 -0
  8. package/dist/endpoints/resource.js +82 -0
  9. package/dist/endpoints/resources-definition.d.ts +3 -0
  10. package/dist/endpoints/resources-definition.js +54 -0
  11. package/dist/endpoints/resources-insert.d.ts +3 -0
  12. package/dist/endpoints/resources-insert.js +174 -0
  13. package/dist/endpoints/resources-sync.d.ts +3 -0
  14. package/dist/endpoints/resources-sync.js +59 -0
  15. package/dist/endpoints/resources.d.ts +3 -0
  16. package/dist/endpoints/resources.js +107 -0
  17. package/dist/endpoints/settings.d.ts +3 -0
  18. package/dist/endpoints/settings.js +34 -0
  19. package/dist/hooks/after-change.d.ts +11 -0
  20. package/dist/hooks/after-change.js +40 -0
  21. package/dist/index.d.ts +6 -0
  22. package/dist/index.js +82 -0
  23. package/dist/types.d.ts +238 -0
  24. package/dist/types.js +12 -0
  25. package/dist/utils/auth.d.ts +3 -0
  26. package/dist/utils/auth.js +27 -0
  27. package/dist/utils/cursor.d.ts +7 -0
  28. package/dist/utils/cursor.js +30 -0
  29. package/dist/utils/fields.d.ts +58 -0
  30. package/dist/utils/fields.js +715 -0
  31. package/dist/utils/json-extract.d.ts +37 -0
  32. package/dist/utils/json-extract.js +186 -0
  33. package/dist/utils/labels.d.ts +12 -0
  34. package/dist/utils/labels.js +27 -0
  35. package/dist/utils/path-resolver.d.ts +53 -0
  36. package/dist/utils/path-resolver.js +157 -0
  37. package/dist/utils/payload-helpers.d.ts +16 -0
  38. package/dist/utils/payload-helpers.js +45 -0
  39. package/package.json +57 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Key-driven extraction for richText / json field values.
3
+ *
4
+ * Walks a JSON value and emits a flat map keyed by JSON Pointer (RFC 6901):
5
+ * { "/root/children/0/text": "Hello" }
6
+ *
7
+ * Only leaf strings whose *key path* (object keys only, array indices skipped)
8
+ * matches one of the compiled glob patterns are emitted.
9
+ *
10
+ * Pattern syntax:
11
+ * text — bare key; sugar for `**.text` (match at any depth)
12
+ * root.foo — anchored path from the root
13
+ * foo.*.bar — `*` matches a single key segment
14
+ * foo.**.bar — `**` matches zero or more key segments
15
+ */
16
+ type Segment = {
17
+ kind: 'key';
18
+ value: string;
19
+ } | {
20
+ kind: 'star';
21
+ } | {
22
+ kind: 'globstar';
23
+ };
24
+ export type CompiledPattern = readonly Segment[];
25
+ export declare function compilePattern(pattern: string): CompiledPattern;
26
+ export interface KeyMatcher {
27
+ matches(keyPath: readonly string[]): boolean;
28
+ }
29
+ export declare function compileKeyMatcher(patterns: readonly string[]): KeyMatcher;
30
+ export declare function extractByKeys(value: unknown, matcher: KeyMatcher): Record<string, string>;
31
+ /**
32
+ * Deep-clones `sourceValue` and writes each translated string at its pointer.
33
+ * Missing targets are skipped silently — the source tree may have drifted.
34
+ */
35
+ export declare function applyByKeys(sourceValue: unknown, translations: Record<string, string>): unknown;
36
+ export declare const DEFAULT_RICHTEXT_KEYS: readonly ["text", "url", "alt"];
37
+ export {};
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Key-driven extraction for richText / json field values.
3
+ *
4
+ * Walks a JSON value and emits a flat map keyed by JSON Pointer (RFC 6901):
5
+ * { "/root/children/0/text": "Hello" }
6
+ *
7
+ * Only leaf strings whose *key path* (object keys only, array indices skipped)
8
+ * matches one of the compiled glob patterns are emitted.
9
+ *
10
+ * Pattern syntax:
11
+ * text — bare key; sugar for `**.text` (match at any depth)
12
+ * root.foo — anchored path from the root
13
+ * foo.*.bar — `*` matches a single key segment
14
+ * foo.**.bar — `**` matches zero or more key segments
15
+ */
16
+ export function compilePattern(pattern) {
17
+ const parts = pattern.split('.').filter((p) => p.length > 0);
18
+ if (parts.length === 1 && parts[0] !== '*' && parts[0] !== '**') {
19
+ return [{ kind: 'globstar' }, { kind: 'key', value: parts[0] }];
20
+ }
21
+ return parts.map((p) => {
22
+ if (p === '*') {
23
+ return { kind: 'star' };
24
+ }
25
+ if (p === '**') {
26
+ return { kind: 'globstar' };
27
+ }
28
+ return { kind: 'key', value: p };
29
+ });
30
+ }
31
+ /** Classic two-pointer glob match with `**` backtracking. */
32
+ function matchPattern(segs, path) {
33
+ let i = 0;
34
+ let j = 0;
35
+ let starI = -1;
36
+ let starJ = 0;
37
+ while (j < path.length) {
38
+ const seg = segs[i];
39
+ if (seg?.kind === 'globstar') {
40
+ starI = i;
41
+ starJ = j;
42
+ i++;
43
+ continue;
44
+ }
45
+ if (seg && (seg.kind === 'star' || (seg.kind === 'key' && seg.value === path[j]))) {
46
+ i++;
47
+ j++;
48
+ continue;
49
+ }
50
+ if (starI !== -1) {
51
+ i = starI + 1;
52
+ starJ++;
53
+ j = starJ;
54
+ continue;
55
+ }
56
+ return false;
57
+ }
58
+ while (i < segs.length && segs[i].kind === 'globstar') {
59
+ i++;
60
+ }
61
+ return i === segs.length;
62
+ }
63
+ export function compileKeyMatcher(patterns) {
64
+ const compiled = patterns.map(compilePattern);
65
+ return {
66
+ matches(keyPath) {
67
+ if (keyPath.length === 0) {
68
+ return false;
69
+ }
70
+ for (const p of compiled) {
71
+ if (matchPattern(p, keyPath)) {
72
+ return true;
73
+ }
74
+ }
75
+ return false;
76
+ },
77
+ };
78
+ }
79
+ function encodePointerSegment(seg) {
80
+ if (typeof seg === 'number') {
81
+ return String(seg);
82
+ }
83
+ return seg.replace(/~/g, '~0').replace(/\//g, '~1');
84
+ }
85
+ function decodePointerSegment(seg) {
86
+ return seg.replace(/~1/g, '/').replace(/~0/g, '~');
87
+ }
88
+ function encodePointer(path) {
89
+ let out = '';
90
+ for (const seg of path) {
91
+ out = `${out}/${encodePointerSegment(seg)}`;
92
+ }
93
+ return out;
94
+ }
95
+ function decodePointer(pointer) {
96
+ if (pointer === '') {
97
+ return [];
98
+ }
99
+ if (pointer[0] !== '/') {
100
+ throw new Error(`Invalid JSON Pointer: ${pointer}`);
101
+ }
102
+ return pointer.slice(1).split('/').map(decodePointerSegment);
103
+ }
104
+ export function extractByKeys(value, matcher) {
105
+ const out = {};
106
+ const locPath = [];
107
+ const keyPath = [];
108
+ const visit = (node) => {
109
+ if (typeof node === 'string') {
110
+ if (matcher.matches(keyPath)) {
111
+ out[encodePointer(locPath)] = node;
112
+ }
113
+ return;
114
+ }
115
+ if (Array.isArray(node)) {
116
+ for (let i = 0; i < node.length; i++) {
117
+ locPath.push(i);
118
+ visit(node[i]);
119
+ locPath.pop();
120
+ }
121
+ return;
122
+ }
123
+ if (node !== null && typeof node === 'object') {
124
+ for (const k of Object.keys(node)) {
125
+ locPath.push(k);
126
+ keyPath.push(k);
127
+ visit(node[k]);
128
+ keyPath.pop();
129
+ locPath.pop();
130
+ }
131
+ }
132
+ };
133
+ visit(value);
134
+ return out;
135
+ }
136
+ /**
137
+ * Deep-clones `sourceValue` and writes each translated string at its pointer.
138
+ * Missing targets are skipped silently — the source tree may have drifted.
139
+ */
140
+ export function applyByKeys(sourceValue, translations) {
141
+ if (sourceValue === null || typeof sourceValue !== 'object') {
142
+ return sourceValue;
143
+ }
144
+ const cloned = structuredClone(sourceValue);
145
+ for (const pointer of Object.keys(translations)) {
146
+ writeAtPointer(cloned, decodePointer(pointer), translations[pointer]);
147
+ }
148
+ return cloned;
149
+ }
150
+ function writeAtPointer(root, segments, value) {
151
+ if (segments.length === 0) {
152
+ return;
153
+ }
154
+ let current = root;
155
+ for (let i = 0; i < segments.length - 1; i++) {
156
+ current = stepInto(current, segments[i]);
157
+ if (current === undefined) {
158
+ return;
159
+ }
160
+ }
161
+ const last = segments[segments.length - 1];
162
+ if (Array.isArray(current)) {
163
+ const idx = Number(last);
164
+ if (Number.isInteger(idx) && idx >= 0 && idx < current.length) {
165
+ current[idx] = value;
166
+ }
167
+ return;
168
+ }
169
+ if (current !== null && typeof current === 'object' && last in current) {
170
+ current[last] = value;
171
+ }
172
+ }
173
+ function stepInto(node, segment) {
174
+ if (Array.isArray(node)) {
175
+ const idx = Number(segment);
176
+ if (!Number.isInteger(idx)) {
177
+ return undefined;
178
+ }
179
+ return node[idx];
180
+ }
181
+ if (node !== null && typeof node === 'object') {
182
+ return node[segment];
183
+ }
184
+ return undefined;
185
+ }
186
+ export const DEFAULT_RICHTEXT_KEYS = ['text', 'url', 'alt'];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resolves a PayloadCMS static label to a plain string.
3
+ *
4
+ * Labels in Payload can be:
5
+ * - `string`: "Post"
6
+ * - `Record<string, string>`: { en: "Post", fr: "Article" }
7
+ * - `function`: (args) => string (used for dynamic labels in admin UI)
8
+ * - `false` | `undefined`
9
+ *
10
+ * We pick the first available value: `en` key, then first key, then fallback.
11
+ */
12
+ export declare function resolveStaticLabel(label: unknown, fallback: string): string;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Resolves a PayloadCMS static label to a plain string.
3
+ *
4
+ * Labels in Payload can be:
5
+ * - `string`: "Post"
6
+ * - `Record<string, string>`: { en: "Post", fr: "Article" }
7
+ * - `function`: (args) => string (used for dynamic labels in admin UI)
8
+ * - `false` | `undefined`
9
+ *
10
+ * We pick the first available value: `en` key, then first key, then fallback.
11
+ */
12
+ export function resolveStaticLabel(label, fallback) {
13
+ if (typeof label === 'string') {
14
+ return label;
15
+ }
16
+ if (label && typeof label === 'object' && !Array.isArray(label)) {
17
+ const record = label;
18
+ if (typeof record.en === 'string') {
19
+ return record.en;
20
+ }
21
+ const firstKey = Object.keys(record)[0];
22
+ if (firstKey && typeof record[firstKey] === 'string') {
23
+ return record[firstKey];
24
+ }
25
+ }
26
+ return fallback;
27
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Structural traversal for "container" fields.
3
+ *
4
+ * In the per-top-level-field model, every translatable resource entry is a
5
+ * single top-level field. Scalars ship as plain strings. Containers (groups,
6
+ * arrays, blocks, richText, json) ship as a JSON-stringified map of
7
+ * { <jsonPointer>: <translatableString> }
8
+ * where each pointer addresses one atomic translatable leaf inside the
9
+ * container's value.
10
+ *
11
+ * `LeafSegment` describes how to navigate from a container's *value* down to
12
+ * one of its localized leaves. Unlike absolute paths from the document root,
13
+ * an `iterate` segment means "the current node is an array, iterate it" —
14
+ * because the container's value IS the array we're already inside.
15
+ */
16
+ export type LeafSegment = {
17
+ kind: 'key';
18
+ name: string;
19
+ } | {
20
+ kind: 'iterate';
21
+ } | {
22
+ kind: 'iterateBlock';
23
+ blockSlug: string;
24
+ };
25
+ export interface LeafLocation {
26
+ /** RFC 6901 JSON Pointer string from the container's value root. */
27
+ pointer: string;
28
+ /** Decoded pointer parts (object keys / numeric indices). */
29
+ pointerParts: string[];
30
+ /** Resolved value at that location. */
31
+ value: unknown;
32
+ }
33
+ export declare function resolveLeafLocations(containerValue: unknown, segments: readonly LeafSegment[]): LeafLocation[];
34
+ export declare function encodePointer(parts: readonly string[]): string;
35
+ export declare function decodePointer(pointer: string): string[];
36
+ /**
37
+ * Concatenate a parent pointer with a child pointer (already encoded).
38
+ * `''` is the empty pointer (root). Either side may be empty.
39
+ */
40
+ export declare function joinPointers(parent: string, child: string): string;
41
+ /**
42
+ * Writes a string at the location addressed by `pointer` inside `target`.
43
+ * Creates intermediate objects/arrays when traversal hits null/undefined,
44
+ * preserving any pre-existing structure encountered along the way.
45
+ */
46
+ export declare function writeAtPointer(target: unknown, pointer: string, value: string): unknown;
47
+ /**
48
+ * Deep-clones the source-locale container value and overlays each translation
49
+ * at its addressed pointer. When `containerSource` is missing, builds a sparse
50
+ * tree from the pointers — best-effort fallback so the platform still receives
51
+ * translated values for any leaves Reversia did send.
52
+ */
53
+ export declare function applyTranslationsToContainer(containerSource: unknown, translations: Record<string, string>): unknown;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Structural traversal for "container" fields.
3
+ *
4
+ * In the per-top-level-field model, every translatable resource entry is a
5
+ * single top-level field. Scalars ship as plain strings. Containers (groups,
6
+ * arrays, blocks, richText, json) ship as a JSON-stringified map of
7
+ * { <jsonPointer>: <translatableString> }
8
+ * where each pointer addresses one atomic translatable leaf inside the
9
+ * container's value.
10
+ *
11
+ * `LeafSegment` describes how to navigate from a container's *value* down to
12
+ * one of its localized leaves. Unlike absolute paths from the document root,
13
+ * an `iterate` segment means "the current node is an array, iterate it" —
14
+ * because the container's value IS the array we're already inside.
15
+ */
16
+ export function resolveLeafLocations(containerValue, segments) {
17
+ const out = [];
18
+ walk(containerValue, segments, 0, [], out);
19
+ return out;
20
+ }
21
+ function walk(node, segs, i, parts, out) {
22
+ if (i === segs.length) {
23
+ out.push({ pointer: encodePointer(parts), pointerParts: parts.slice(), value: node });
24
+ return;
25
+ }
26
+ if (node === null || node === undefined) {
27
+ return;
28
+ }
29
+ const seg = segs[i];
30
+ if (seg.kind === 'key') {
31
+ if (typeof node !== 'object' || Array.isArray(node)) {
32
+ return;
33
+ }
34
+ const next = node[seg.name];
35
+ walk(next, segs, i + 1, [...parts, seg.name], out);
36
+ return;
37
+ }
38
+ if (seg.kind === 'iterate') {
39
+ if (!Array.isArray(node)) {
40
+ return;
41
+ }
42
+ for (let idx = 0; idx < node.length; idx++) {
43
+ walk(node[idx], segs, i + 1, [...parts, String(idx)], out);
44
+ }
45
+ return;
46
+ }
47
+ if (seg.kind === 'iterateBlock') {
48
+ if (!Array.isArray(node)) {
49
+ return;
50
+ }
51
+ for (let idx = 0; idx < node.length; idx++) {
52
+ const item = node[idx];
53
+ if (item &&
54
+ typeof item === 'object' &&
55
+ item.blockType === seg.blockSlug) {
56
+ walk(item, segs, i + 1, [...parts, String(idx)], out);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ export function encodePointer(parts) {
62
+ if (parts.length === 0) {
63
+ return '';
64
+ }
65
+ let out = '';
66
+ for (const p of parts) {
67
+ out += `/${encodePointerSegment(p)}`;
68
+ }
69
+ return out;
70
+ }
71
+ export function decodePointer(pointer) {
72
+ if (pointer === '' || pointer === '/') {
73
+ return pointer === '/' ? [''] : [];
74
+ }
75
+ if (pointer[0] !== '/') {
76
+ throw new Error(`Invalid JSON Pointer: ${pointer}`);
77
+ }
78
+ return pointer.slice(1).split('/').map(decodePointerSegment);
79
+ }
80
+ function encodePointerSegment(seg) {
81
+ return seg.replace(/~/g, '~0').replace(/\//g, '~1');
82
+ }
83
+ function decodePointerSegment(seg) {
84
+ return seg.replace(/~1/g, '/').replace(/~0/g, '~');
85
+ }
86
+ /**
87
+ * Concatenate a parent pointer with a child pointer (already encoded).
88
+ * `''` is the empty pointer (root). Either side may be empty.
89
+ */
90
+ export function joinPointers(parent, child) {
91
+ if (parent === '') {
92
+ return child;
93
+ }
94
+ if (child === '') {
95
+ return parent;
96
+ }
97
+ return `${parent}${child}`;
98
+ }
99
+ /**
100
+ * Writes a string at the location addressed by `pointer` inside `target`.
101
+ * Creates intermediate objects/arrays when traversal hits null/undefined,
102
+ * preserving any pre-existing structure encountered along the way.
103
+ */
104
+ export function writeAtPointer(target, pointer, value) {
105
+ const parts = decodePointer(pointer);
106
+ if (parts.length === 0) {
107
+ return value;
108
+ }
109
+ let root = target;
110
+ if (root === null || root === undefined) {
111
+ root = /^\d+$/.test(parts[0]) ? [] : {};
112
+ }
113
+ let current = root;
114
+ for (let i = 0; i < parts.length - 1; i++) {
115
+ const key = parts[i];
116
+ const nextKey = parts[i + 1];
117
+ const nextIsIndex = /^\d+$/.test(nextKey);
118
+ if (Array.isArray(current)) {
119
+ const idx = Number(key);
120
+ if (current[idx] === null || current[idx] === undefined) {
121
+ current[idx] = nextIsIndex ? [] : {};
122
+ }
123
+ current = current[idx];
124
+ continue;
125
+ }
126
+ if (current && typeof current === 'object') {
127
+ const obj = current;
128
+ if (obj[key] === null || obj[key] === undefined) {
129
+ obj[key] = nextIsIndex ? [] : {};
130
+ }
131
+ current = obj[key];
132
+ }
133
+ }
134
+ const last = parts[parts.length - 1];
135
+ if (Array.isArray(current)) {
136
+ current[Number(last)] = value;
137
+ }
138
+ else if (current && typeof current === 'object') {
139
+ current[last] = value;
140
+ }
141
+ return root;
142
+ }
143
+ /**
144
+ * Deep-clones the source-locale container value and overlays each translation
145
+ * at its addressed pointer. When `containerSource` is missing, builds a sparse
146
+ * tree from the pointers — best-effort fallback so the platform still receives
147
+ * translated values for any leaves Reversia did send.
148
+ */
149
+ export function applyTranslationsToContainer(containerSource, translations) {
150
+ let root = containerSource === undefined || containerSource === null
151
+ ? null
152
+ : structuredClone(containerSource);
153
+ for (const [pointer, value] of Object.entries(translations)) {
154
+ root = writeAtPointer(root, pointer, value);
155
+ }
156
+ return root;
157
+ }
@@ -0,0 +1,16 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ /**
3
+ * Parses a caller-supplied `limit` query parameter and clamps it to a safe
4
+ * range. NaN, zero, and negatives fall back to the default.
5
+ */
6
+ export declare function parseLimit(raw: string | null, fallback?: number): number;
7
+ /**
8
+ * Resolves the default locale code from a Payload config, defaulting to `en`
9
+ * when localization is not configured.
10
+ */
11
+ export declare function resolveDefaultLocale(req: PayloadRequest): string;
12
+ /**
13
+ * Plain dot-path lookup on an object, returning `undefined` for any missing
14
+ * segment or non-object traversal. Does not walk into arrays.
15
+ */
16
+ export declare function getNestedValue(obj: Record<string, unknown>, path: string): unknown;
@@ -0,0 +1,45 @@
1
+ const DEFAULT_LIMIT = 100;
2
+ const MAX_LIMIT = 1000;
3
+ /**
4
+ * Parses a caller-supplied `limit` query parameter and clamps it to a safe
5
+ * range. NaN, zero, and negatives fall back to the default.
6
+ */
7
+ export function parseLimit(raw, fallback = DEFAULT_LIMIT) {
8
+ if (!raw) {
9
+ return fallback;
10
+ }
11
+ const parsed = Number.parseInt(raw, 10);
12
+ if (!Number.isFinite(parsed) || parsed <= 0) {
13
+ return fallback;
14
+ }
15
+ return Math.min(parsed, MAX_LIMIT);
16
+ }
17
+ /**
18
+ * Resolves the default locale code from a Payload config, defaulting to `en`
19
+ * when localization is not configured.
20
+ */
21
+ export function resolveDefaultLocale(req) {
22
+ const localization = req.payload.config.localization;
23
+ if (localization && typeof localization === 'object' && 'defaultLocale' in localization) {
24
+ const value = localization.defaultLocale;
25
+ if (typeof value === 'string' && value.length > 0) {
26
+ return value;
27
+ }
28
+ }
29
+ return 'en';
30
+ }
31
+ /**
32
+ * Plain dot-path lookup on an object, returning `undefined` for any missing
33
+ * segment or non-object traversal. Does not walk into arrays.
34
+ */
35
+ export function getNestedValue(obj, path) {
36
+ const parts = path.split('.');
37
+ let current = obj;
38
+ for (const part of parts) {
39
+ if (current === null || current === undefined || typeof current !== 'object') {
40
+ return undefined;
41
+ }
42
+ current = current[part];
43
+ }
44
+ return current;
45
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@sequoialabs/payload-plugin-reversia",
3
+ "version": "0.1.0",
4
+ "author": {
5
+ "name": "Jean Walrave",
6
+ "email": "contact@reversia.tech",
7
+ "url": "https://reversia.tech"
8
+ },
9
+ "license": "MIT",
10
+ "homepage": "https://github.com/SequoiaLabs/reversia-payloadcms-plugin#readme",
11
+ "keywords": [
12
+ "payload",
13
+ "payloadcms",
14
+ "plugin",
15
+ "reversia",
16
+ "i18n",
17
+ "translation",
18
+ "localization"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/SequoiaLabs/reversia-payloadcms-plugin.git"
23
+ },
24
+ "description": "PayloadCMS plugin for Reversia translation SaaS integration",
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ }
31
+ },
32
+ "main": "dist/index.js",
33
+ "types": "dist/index.d.ts",
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "dev": "tsc -p tsconfig.build.json --watch",
40
+ "test": "rm -f dev/test.db && bun test",
41
+ "lint": "biome check",
42
+ "lint:fix": "biome check --write",
43
+ "format": "biome format --write",
44
+ "prepublishOnly": "bun run test && bun run build"
45
+ },
46
+ "peerDependencies": {
47
+ "payload": "^3.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@biomejs/biome": "2.4.12",
51
+ "@payloadcms/db-sqlite": "^3.0.0",
52
+ "@payloadcms/richtext-lexical": "^3.79.1",
53
+ "@types/bun": "^1.3.10",
54
+ "payload": "^3.0.0",
55
+ "typescript": "^5.0.0"
56
+ }
57
+ }