@lemonadecms/client 0.0.0-stage → 0.1.1
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/CHANGELOG.md +84 -0
- package/LICENSE +21 -0
- package/README.md +304 -2
- package/bin/cli/config.mjs +309 -0
- package/bin/cli/create-migration.mjs +157 -0
- package/bin/cli/errors.mjs +9 -0
- package/bin/cli/io.mjs +70 -0
- package/bin/cli/migrate.mjs +109 -0
- package/bin/cli/network.mjs +134 -0
- package/bin/cli/picker.mjs +118 -0
- package/bin/cli/run.mjs +240 -0
- package/bin/cli/schema.mjs +397 -0
- package/bin/codegen/csharp.mjs +180 -0
- package/bin/codegen/definitions.mjs +52 -0
- package/bin/codegen/languages.mjs +16 -0
- package/bin/codegen/python.mjs +125 -0
- package/bin/generate-types.mjs +202 -0
- package/bin/lemonade.mjs +11 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.js +39 -0
- package/dist/images.d.ts +11 -0
- package/dist/images.js +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +17 -0
- package/dist/preview.d.ts +11 -0
- package/dist/preview.js +92 -0
- package/dist/rich-text.d.ts +4 -0
- package/dist/rich-text.js +228 -0
- package/dist/server.browser.d.ts +2 -0
- package/dist/server.browser.js +9 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +17 -0
- package/dist/transport.d.ts +3 -0
- package/dist/transport.js +519 -0
- package/dist/types.d.ts +202 -0
- package/dist/types.js +6 -0
- package/package.json +52 -4
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
export type ModelMap<Models> = {
|
|
2
|
+
[Key in keyof Models]: object;
|
|
3
|
+
};
|
|
4
|
+
export type UntypedModels = Record<string, Record<string, unknown>>;
|
|
5
|
+
export type FieldName<Fields> = Extract<keyof Fields, string>;
|
|
6
|
+
export interface CMSBaseOptions {
|
|
7
|
+
/** Requests always use https://lemonadecms.com. Custom CMS hosts are unsupported. */
|
|
8
|
+
url?: never;
|
|
9
|
+
projectId: string;
|
|
10
|
+
/** Compatible transport for SSR, testing or polyfills; receives production URLs. */
|
|
11
|
+
fetch?: typeof globalThis.fetch;
|
|
12
|
+
/**
|
|
13
|
+
* Optional per-request deadline, including JSON reading: 1–2147483647 ms.
|
|
14
|
+
* No default deadline. Combining with a caller signal requires AbortSignal.any.
|
|
15
|
+
*/
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface CMSClientOptions extends CMSBaseOptions {
|
|
19
|
+
publicKey: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CMSPreviewOptions extends CMSBaseOptions {
|
|
22
|
+
secretKey: string;
|
|
23
|
+
}
|
|
24
|
+
type Scalar = string | number | boolean;
|
|
25
|
+
export type ScalarField<Fields> = {
|
|
26
|
+
[Key in FieldName<Fields>]: unknown extends Fields[Key] ? Key : NonNullable<Fields[Key]> extends Scalar ? Key : never;
|
|
27
|
+
}[FieldName<Fields>];
|
|
28
|
+
type ScalarContentFilter<Fields> = {
|
|
29
|
+
[Key in ScalarField<Fields>]: {
|
|
30
|
+
field: Key;
|
|
31
|
+
op: "eq" | "ne";
|
|
32
|
+
value: Extract<Fields[Key], Scalar> | (unknown extends Fields[Key] ? Scalar : never) | null;
|
|
33
|
+
} | {
|
|
34
|
+
field: Key;
|
|
35
|
+
op: "in" | "not_in";
|
|
36
|
+
value: readonly (Extract<Fields[Key], Scalar> | (unknown extends Fields[Key] ? Scalar : never))[];
|
|
37
|
+
} | (NonNullable<Fields[Key]> extends boolean ? never : {
|
|
38
|
+
field: Key;
|
|
39
|
+
op: "gt" | "gte" | "lt" | "lte";
|
|
40
|
+
value: Extract<Fields[Key], string | number> | (unknown extends Fields[Key] ? string | number : never);
|
|
41
|
+
});
|
|
42
|
+
}[ScalarField<Fields>];
|
|
43
|
+
type ChoicesField<Fields> = {
|
|
44
|
+
[Key in FieldName<Fields>]: unknown extends Fields[Key] ? Key : NonNullable<Fields[Key]> extends readonly string[] ? Key : never;
|
|
45
|
+
}[FieldName<Fields>];
|
|
46
|
+
type ChoicesValue<Value> = NonNullable<Value> extends readonly (infer Option extends string)[] ? Option : string;
|
|
47
|
+
type ChoicesContentFilter<Fields> = {
|
|
48
|
+
[Key in ChoicesField<Fields>]: {
|
|
49
|
+
field: Key;
|
|
50
|
+
op: "contains_any" | "contains_all";
|
|
51
|
+
value: readonly ChoicesValue<Fields[Key]>[];
|
|
52
|
+
};
|
|
53
|
+
}[ChoicesField<Fields>];
|
|
54
|
+
export type TextField<Fields> = {
|
|
55
|
+
[Key in FieldName<Fields>]: unknown extends Fields[Key] ? Key : NonNullable<Fields[Key]> extends string ? Key : never;
|
|
56
|
+
}[FieldName<Fields>];
|
|
57
|
+
type TextContentFilter<Fields> = {
|
|
58
|
+
[Key in TextField<Fields>]: {
|
|
59
|
+
field: Key;
|
|
60
|
+
op: "eqi" | "contains" | "containsi" | "starts_with" | "starts_withi" | "ends_with" | "ends_withi";
|
|
61
|
+
value: string;
|
|
62
|
+
};
|
|
63
|
+
}[TextField<Fields>];
|
|
64
|
+
export type ContentFilter<Fields> = ScalarContentFilter<Fields> | ChoicesContentFilter<Fields> | TextContentFilter<Fields> | {
|
|
65
|
+
and: readonly ContentFilter<Fields>[];
|
|
66
|
+
} | {
|
|
67
|
+
or: readonly ContentFilter<Fields>[];
|
|
68
|
+
} | {
|
|
69
|
+
not: ContentFilter<Fields>;
|
|
70
|
+
};
|
|
71
|
+
export interface ContentSearch<Fields = Record<string, unknown>> {
|
|
72
|
+
query: string;
|
|
73
|
+
/** Top-level text/long_text/markdown fields; the backend validates stored types. */
|
|
74
|
+
fields?: readonly TextField<Fields>[];
|
|
75
|
+
}
|
|
76
|
+
export interface ContentSort<Fields> {
|
|
77
|
+
field: ScalarField<Fields>;
|
|
78
|
+
direction: "asc" | "desc";
|
|
79
|
+
}
|
|
80
|
+
interface BaseListOptions<Key extends string, Fields> {
|
|
81
|
+
select?: readonly Key[];
|
|
82
|
+
locale?: string;
|
|
83
|
+
limit?: number;
|
|
84
|
+
/** Ten leaves, depth four, 31 nodes and 4 KiB. Arrays remain implicit AND. */
|
|
85
|
+
filters?: readonly ContentFilter<Fields>[] | ContentFilter<Fields>;
|
|
86
|
+
/** One object or 1–3 unique ordered fields; the UUID breaks remaining ties. */
|
|
87
|
+
sort?: ContentSort<Fields> | readonly ContentSort<Fields>[];
|
|
88
|
+
search?: ContentSearch<Fields>;
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
/** Explicit live enrichment, independently cached from the saved publication. */
|
|
91
|
+
includeRemote?: boolean;
|
|
92
|
+
}
|
|
93
|
+
/** Follow a returned boundary in exactly one direction; or request a numbered page; modes cannot be combined. */
|
|
94
|
+
export type ListOptions<Key extends string = string, Fields = Record<string, unknown>> = BaseListOptions<Key, Fields> & ({
|
|
95
|
+
after?: string;
|
|
96
|
+
before?: never;
|
|
97
|
+
page?: never;
|
|
98
|
+
pageSize?: never;
|
|
99
|
+
} | {
|
|
100
|
+
before?: string;
|
|
101
|
+
after?: never;
|
|
102
|
+
page?: never;
|
|
103
|
+
pageSize?: never;
|
|
104
|
+
} | {
|
|
105
|
+
page?: number;
|
|
106
|
+
pageSize?: number;
|
|
107
|
+
limit?: never;
|
|
108
|
+
after?: never;
|
|
109
|
+
before?: never;
|
|
110
|
+
});
|
|
111
|
+
/** One first match, with no caller-controlled page size or boundary. */
|
|
112
|
+
export type FindOneOptions<Key extends string = string, Fields = Record<string, unknown>> = Pick<BaseListOptions<Key, Fields>, "select" | "locale" | "filters" | "sort" | "search" | "signal" | "includeRemote">;
|
|
113
|
+
/** Direct statistics have no pagination, projection, sorting or remote enrichment. */
|
|
114
|
+
export type CountOptions<Fields = Record<string, unknown>> = Pick<BaseListOptions<string, Fields>, "locale" | "filters" | "search" | "signal">;
|
|
115
|
+
export type GetManyOptions<Key extends string = string> = Pick<BaseListOptions<Key, Record<string, unknown>>, "select" | "locale" | "signal" | "includeRemote">;
|
|
116
|
+
/** A paginated search; fields scopes matching, select projects the returned fields. */
|
|
117
|
+
export type SearchOptions<Key extends string = string, Fields = Record<string, unknown>> = ListOptions<Key, Fields> & {
|
|
118
|
+
fields?: readonly TextField<Fields>[];
|
|
119
|
+
search?: never;
|
|
120
|
+
};
|
|
121
|
+
/** Lazy forward traversal, capped at 1,000 pages by default (maximum 10,000). */
|
|
122
|
+
export type PaginationOptions<Key extends string = string, Fields = Record<string, unknown>> = Omit<ListOptions<Key, Fields>, "before" | "page" | "pageSize"> & {
|
|
123
|
+
page?: never;
|
|
124
|
+
pageSize?: never;
|
|
125
|
+
maxPages?: number;
|
|
126
|
+
};
|
|
127
|
+
/** Identity of the selected model; ID/name can be absent on older CMS responses. */
|
|
128
|
+
export interface ContentTypeIdentity<Model extends string = string> {
|
|
129
|
+
key: Model;
|
|
130
|
+
id?: string;
|
|
131
|
+
name?: string;
|
|
132
|
+
}
|
|
133
|
+
export interface ContentEnvironment {
|
|
134
|
+
id: string;
|
|
135
|
+
key: string;
|
|
136
|
+
}
|
|
137
|
+
/** Metadata sits beside modeled fields and is retained by field projections. */
|
|
138
|
+
export interface ContentEntry<Fields, Model extends string = string> {
|
|
139
|
+
id: string;
|
|
140
|
+
fields: Fields;
|
|
141
|
+
publishedAt: string | null;
|
|
142
|
+
/** Optional in this interface to preserve existing manually constructed entries. */
|
|
143
|
+
contentType?: ContentTypeIdentity<Model>;
|
|
144
|
+
projectId?: string;
|
|
145
|
+
/** Resolved request locale; fallback fields can originate in the base language. */
|
|
146
|
+
locale?: string;
|
|
147
|
+
/** Absent when the CMS response predates environment metadata. */
|
|
148
|
+
environment?: ContentEnvironment;
|
|
149
|
+
remote?: RemoteValue | null;
|
|
150
|
+
}
|
|
151
|
+
export interface RemoteValue<Data = Record<string, string | number | boolean | null>> {
|
|
152
|
+
source: string;
|
|
153
|
+
status: "available" | "error" | "disabled" | "not_linked";
|
|
154
|
+
readOnly: true;
|
|
155
|
+
stale: boolean;
|
|
156
|
+
data: Data | null;
|
|
157
|
+
error: string | null;
|
|
158
|
+
fetchedAt: string | null;
|
|
159
|
+
expiresAt: string | null;
|
|
160
|
+
}
|
|
161
|
+
export interface ContentPage<Fields, Model extends string = string> {
|
|
162
|
+
data: ContentEntry<Fields, Model>[];
|
|
163
|
+
meta: {
|
|
164
|
+
nextCursor: string | null;
|
|
165
|
+
limit: number;
|
|
166
|
+
page?: number;
|
|
167
|
+
pageSize?: number;
|
|
168
|
+
previousCursor?: string | null;
|
|
169
|
+
hasNextPage?: boolean;
|
|
170
|
+
hasPreviousPage?: boolean;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export interface ContentCollection<Fields extends object, Model extends string = string> {
|
|
174
|
+
list<const Key extends FieldName<Fields> = FieldName<Fields>>(options?: ListOptions<Key, Fields>): Promise<ContentPage<Pick<Fields, Key>, Model>>;
|
|
175
|
+
/** One page of plain text matches; relevance order unless an explicit sort is supplied. */
|
|
176
|
+
search<const Key extends FieldName<Fields> = FieldName<Fields>>(query: string, options?: SearchOptions<Key, Fields>): Promise<ContentPage<Pick<Fields, Key>, Model>>;
|
|
177
|
+
/** First matching entry or null; all request and response errors still reject. */
|
|
178
|
+
findOne<const Key extends FieldName<Fields> = FieldName<Fields>>(options?: FindOneOptions<Key, Fields>): Promise<ContentEntry<Pick<Fields, Key>, Model> | null>;
|
|
179
|
+
/** Required first match; CMSNotFoundError only for a successful empty query. */
|
|
180
|
+
findOneOrThrow<const Key extends FieldName<Fields> = FieldName<Fields>>(options?: FindOneOptions<Key, Fields>): Promise<ContentEntry<Pick<Fields, Key>, Model>>;
|
|
181
|
+
/** Matching entries across all pages, counted by the backend without downloading fields. */
|
|
182
|
+
count(options?: CountOptions<Fields>): Promise<number>;
|
|
183
|
+
/** Whether any entry matches; backend stops after the first match. */
|
|
184
|
+
exists(options?: CountOptions<Fields>): Promise<boolean>;
|
|
185
|
+
/** At most 100 UUIDs in one request; preserves order/duplicates, missing entries become null. */
|
|
186
|
+
getMany<const Key extends FieldName<Fields> = FieldName<Fields>>(ids: readonly string[], options?: GetManyOptions<Key>): Promise<(ContentEntry<Pick<Fields, Key>, Model> | null)[]>;
|
|
187
|
+
/** Fetch only as the caller requests another page. Breaking stops network work. */
|
|
188
|
+
pages<const Key extends FieldName<Fields> = FieldName<Fields>>(options?: PaginationOptions<Key, Fields>): AsyncIterableIterator<ContentPage<Pick<Fields, Key>, Model>>;
|
|
189
|
+
/** Flatten pages lazily, retaining at most one page of entries. */
|
|
190
|
+
entries<const Key extends FieldName<Fields> = FieldName<Fields>>(options?: PaginationOptions<Key, Fields>): AsyncIterableIterator<ContentEntry<Pick<Fields, Key>, Model>>;
|
|
191
|
+
get<const Key extends FieldName<Fields> = FieldName<Fields>>(id: string, options?: Pick<ListOptions<Key, Fields>, "select" | "locale" | "signal" | "includeRemote">): Promise<ContentEntry<Pick<Fields, Key>, Model>>;
|
|
192
|
+
}
|
|
193
|
+
export interface CMSClient<Models extends ModelMap<Models> = UntypedModels> {
|
|
194
|
+
content<Model extends Extract<keyof Models, string>>(model: Model): ContentCollection<Models[Model], Model>;
|
|
195
|
+
}
|
|
196
|
+
/** A draft may omit required fields or contain nulls until it is published. */
|
|
197
|
+
export type PreviewModels<Models> = {
|
|
198
|
+
[Model in keyof Models]: {
|
|
199
|
+
[Field in keyof Models[Model]]?: Models[Model][Field] | null;
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
export {};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// TypeScript contracts for configuration, fluent queries, and returned pages.
|
|
2
|
+
// A caller can supply a model map to get checked model names and selected fields.
|
|
3
|
+
// These types disappear at runtime: they are not schema discovery or validation
|
|
4
|
+
// of arbitrary model payloads. transport.ts validates the response envelope;
|
|
5
|
+
// the CMS backend validates stored field values when they are written/published.
|
|
6
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lemonadecms/client",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Framework-independent content delivery and developer tooling for Lemonade CMS",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://lemonadecms.com/guides/sdk",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/ilara-booqself/lemonadecms.git",
|
|
10
|
+
"directory": "packages/cms-client"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/ilara-booqself/lemonadecms/issues"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public",
|
|
17
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/*.js",
|
|
23
|
+
"dist/*.d.ts",
|
|
24
|
+
"bin/*.mjs",
|
|
25
|
+
"bin/cli/*.mjs",
|
|
26
|
+
"bin/codegen/*.mjs",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"CHANGELOG.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=22"
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"default": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./server": {
|
|
40
|
+
"types": "./dist/server.d.ts",
|
|
41
|
+
"browser": "./dist/server.browser.js",
|
|
42
|
+
"default": "./dist/server.js"
|
|
43
|
+
},
|
|
44
|
+
"./preview": {
|
|
45
|
+
"types": "./dist/preview.d.ts",
|
|
46
|
+
"default": "./dist/preview.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"bin": {
|
|
50
|
+
"cms-codegen": "bin/generate-types.mjs",
|
|
51
|
+
"lemonade": "bin/lemonade.mjs"
|
|
52
|
+
},
|
|
53
|
+
"private": false
|
|
54
|
+
}
|