@lokal-server/ts-sdk 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/README.md +129 -0
- package/contracts/lokal-manifest.schema.json +34 -0
- package/contracts/openapi.json +717 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +3 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @lokal-server/ts-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for Lokal external apps. Lokal is a self-hosted file/data hub; this SDK helps apps define a manifest, authenticate a Lokal user, register the manifest, and read/write app-private JSON data.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @lokal-server/ts-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Define an app
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createLokalClient, defineLokalApp } from '@lokal-server/ts-sdk';
|
|
15
|
+
|
|
16
|
+
const recipeSchema = { type: 'object' };
|
|
17
|
+
const settingsSchema = { type: 'object' };
|
|
18
|
+
|
|
19
|
+
const manifest = defineLokalApp({
|
|
20
|
+
name: 'Recipe Box',
|
|
21
|
+
slug: 'recipe-box',
|
|
22
|
+
collections: {
|
|
23
|
+
recipes: recipeSchema,
|
|
24
|
+
settings: settingsSchema,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const lokal = createLokalClient({
|
|
29
|
+
instanceUrl: 'https://files.example.com',
|
|
30
|
+
manifest,
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`defineLokalApp()` preserves collection names as literal TypeScript keys, so `lokal.collection('recipes')` is accepted and unknown collection names are rejected by TypeScript.
|
|
35
|
+
|
|
36
|
+
## Sign in
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
await lokal.auth.signIn({
|
|
40
|
+
email: 'user@example.com',
|
|
41
|
+
password: 'password',
|
|
42
|
+
tokenName: 'Recipe Box',
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Sign-in posts the manifest to `/api/platform/auth` and stores the returned app token in memory. You can also manage the token yourself:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
lokal.setToken(savedToken);
|
|
50
|
+
const token = lokal.getToken();
|
|
51
|
+
lokal.clearToken();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The SDK does not persist tokens to `localStorage` or `sessionStorage`; consuming apps should choose their own persistence strategy.
|
|
55
|
+
|
|
56
|
+
## Collection records
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const recipes = lokal.collection('recipes');
|
|
60
|
+
|
|
61
|
+
const created = await recipes.create({ title: 'Pancakes' }, { key: 'pancakes' });
|
|
62
|
+
const all = await recipes.list({ limit: 20 });
|
|
63
|
+
const one = await recipes.get(created.id);
|
|
64
|
+
const updated = await recipes.update(created.id, { title: 'Blueberry Pancakes' });
|
|
65
|
+
await recipes.delete(updated.id);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Singleton values
|
|
69
|
+
|
|
70
|
+
Use singleton values for one JSON document per collection, such as settings.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
await lokal.collection('settings').setValue({ theme: 'dark' });
|
|
74
|
+
const settings = await lokal.collection('settings').getValue();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Custom fetch
|
|
78
|
+
|
|
79
|
+
By default the SDK uses global `fetch`. Pass a custom implementation for tests or non-standard runtimes:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const lokal = createLokalClient({
|
|
83
|
+
instanceUrl: 'https://files.example.com',
|
|
84
|
+
manifest,
|
|
85
|
+
fetch: customFetch,
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Errors
|
|
90
|
+
|
|
91
|
+
Missing tokens throw a clear `Error`. Non-2xx API responses throw `LokalApiError` with `status`, `message`, and parsed `body` when available.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { LokalApiError } from 'lokal-ts-sdk';
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await lokal.collection('recipes').list();
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (error instanceof LokalApiError) {
|
|
100
|
+
console.error(error.status, error.body);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Contracts
|
|
106
|
+
|
|
107
|
+
The main Lokal repo owns the API contracts. Copies are kept in `contracts/`:
|
|
108
|
+
|
|
109
|
+
- `contracts/openapi.json`
|
|
110
|
+
- `contracts/lokal-manifest.schema.json`
|
|
111
|
+
|
|
112
|
+
Update them with:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
npm run contracts:update
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
If the OpenAPI contract changes, update SDK types, wrappers, and tests in the same change.
|
|
119
|
+
|
|
120
|
+
## Publishing
|
|
121
|
+
|
|
122
|
+
The package is configured for public npm publishing with `private: false`, `files: ["dist", "contracts", "README.md"]`, and `publishConfig.access: "public"`.
|
|
123
|
+
|
|
124
|
+
Do not publish automatically. When ready, run checks and publish explicitly:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
npm run check
|
|
128
|
+
npm publish
|
|
129
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://lokal.local/contracts/lokal-manifest.schema.json",
|
|
4
|
+
"title": "Lokal app manifest",
|
|
5
|
+
"description": "Serializable shape sent by external apps from defineLokalApp during platform authentication.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["slug", "collections"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"name": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 1
|
|
12
|
+
},
|
|
13
|
+
"slug": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"pattern": "^[a-z0-9-]+$"
|
|
16
|
+
},
|
|
17
|
+
"description": {
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
20
|
+
"developerName": {
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
"collections": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"minProperties": 1,
|
|
26
|
+
"propertyNames": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
29
|
+
},
|
|
30
|
+
"additionalProperties": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"additionalProperties": true
|
|
34
|
+
}
|
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.1.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Lokal Platform API",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Contract for external apps integrating with Lokal as an app-private JSON data store."
|
|
7
|
+
},
|
|
8
|
+
"servers": [
|
|
9
|
+
{
|
|
10
|
+
"url": "{lokalUrl}",
|
|
11
|
+
"variables": {
|
|
12
|
+
"lokalUrl": {
|
|
13
|
+
"default": "https://files.example.com"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"tags": [
|
|
19
|
+
{
|
|
20
|
+
"name": "Discovery"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "Auth"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "Records"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "Values"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"paths": {
|
|
33
|
+
"/.well-known/lokal": {
|
|
34
|
+
"get": {
|
|
35
|
+
"tags": ["Discovery"],
|
|
36
|
+
"operationId": "getLokalDiscovery",
|
|
37
|
+
"summary": "Discover Lokal platform capabilities",
|
|
38
|
+
"responses": {
|
|
39
|
+
"200": {
|
|
40
|
+
"description": "Lokal discovery metadata",
|
|
41
|
+
"content": {
|
|
42
|
+
"application/json": {
|
|
43
|
+
"schema": {
|
|
44
|
+
"$ref": "#/components/schemas/DiscoveryResponse"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"/api/platform/auth": {
|
|
53
|
+
"post": {
|
|
54
|
+
"tags": ["Auth"],
|
|
55
|
+
"operationId": "authenticateApp",
|
|
56
|
+
"summary": "Authenticate a Lokal user and register an app manifest",
|
|
57
|
+
"requestBody": {
|
|
58
|
+
"required": true,
|
|
59
|
+
"content": {
|
|
60
|
+
"application/json": {
|
|
61
|
+
"schema": {
|
|
62
|
+
"$ref": "#/components/schemas/AuthRequest"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"responses": {
|
|
68
|
+
"200": {
|
|
69
|
+
"description": "User-bound app token and registered app metadata",
|
|
70
|
+
"content": {
|
|
71
|
+
"application/json": {
|
|
72
|
+
"schema": {
|
|
73
|
+
"$ref": "#/components/schemas/AuthResponse"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"400": {
|
|
79
|
+
"$ref": "#/components/responses/BadRequest"
|
|
80
|
+
},
|
|
81
|
+
"401": {
|
|
82
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"/api/platform/apps/{appSlug}/collections/{collection}/records": {
|
|
88
|
+
"get": {
|
|
89
|
+
"tags": ["Records"],
|
|
90
|
+
"operationId": "listRecords",
|
|
91
|
+
"summary": "List records in an app-private collection for the authenticated user",
|
|
92
|
+
"security": [
|
|
93
|
+
{
|
|
94
|
+
"bearerAuth": []
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"parameters": [
|
|
98
|
+
{
|
|
99
|
+
"$ref": "#/components/parameters/AppSlug"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"$ref": "#/components/parameters/Collection"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "limit",
|
|
106
|
+
"in": "query",
|
|
107
|
+
"required": false,
|
|
108
|
+
"schema": {
|
|
109
|
+
"type": "integer",
|
|
110
|
+
"minimum": 1,
|
|
111
|
+
"maximum": 100,
|
|
112
|
+
"default": 50
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"responses": {
|
|
117
|
+
"200": {
|
|
118
|
+
"description": "Collection records",
|
|
119
|
+
"content": {
|
|
120
|
+
"application/json": {
|
|
121
|
+
"schema": {
|
|
122
|
+
"type": "array",
|
|
123
|
+
"items": {
|
|
124
|
+
"$ref": "#/components/schemas/DataRecord"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"400": {
|
|
131
|
+
"$ref": "#/components/responses/BadRequest"
|
|
132
|
+
},
|
|
133
|
+
"401": {
|
|
134
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"post": {
|
|
139
|
+
"tags": ["Records"],
|
|
140
|
+
"operationId": "createRecord",
|
|
141
|
+
"summary": "Create a record in an app-private collection for the authenticated user",
|
|
142
|
+
"security": [
|
|
143
|
+
{
|
|
144
|
+
"bearerAuth": []
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
"parameters": [
|
|
148
|
+
{
|
|
149
|
+
"$ref": "#/components/parameters/AppSlug"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"$ref": "#/components/parameters/Collection"
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
"requestBody": {
|
|
156
|
+
"required": true,
|
|
157
|
+
"content": {
|
|
158
|
+
"application/json": {
|
|
159
|
+
"schema": {
|
|
160
|
+
"$ref": "#/components/schemas/RecordWriteRequest"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"responses": {
|
|
166
|
+
"201": {
|
|
167
|
+
"description": "Created record",
|
|
168
|
+
"content": {
|
|
169
|
+
"application/json": {
|
|
170
|
+
"schema": {
|
|
171
|
+
"$ref": "#/components/schemas/DataRecord"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"400": {
|
|
177
|
+
"$ref": "#/components/responses/BadRequest"
|
|
178
|
+
},
|
|
179
|
+
"401": {
|
|
180
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"/api/platform/apps/{appSlug}/collections/{collection}/records/{recordId}": {
|
|
186
|
+
"get": {
|
|
187
|
+
"tags": ["Records"],
|
|
188
|
+
"operationId": "getRecord",
|
|
189
|
+
"summary": "Read one collection record",
|
|
190
|
+
"security": [
|
|
191
|
+
{
|
|
192
|
+
"bearerAuth": []
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
"parameters": [
|
|
196
|
+
{
|
|
197
|
+
"$ref": "#/components/parameters/AppSlug"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"$ref": "#/components/parameters/Collection"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"$ref": "#/components/parameters/RecordId"
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
"responses": {
|
|
207
|
+
"200": {
|
|
208
|
+
"description": "Record",
|
|
209
|
+
"content": {
|
|
210
|
+
"application/json": {
|
|
211
|
+
"schema": {
|
|
212
|
+
"$ref": "#/components/schemas/DataRecord"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
"404": {
|
|
218
|
+
"$ref": "#/components/responses/NotFound"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
"patch": {
|
|
223
|
+
"tags": ["Records"],
|
|
224
|
+
"operationId": "updateRecord",
|
|
225
|
+
"summary": "Update one collection record",
|
|
226
|
+
"security": [
|
|
227
|
+
{
|
|
228
|
+
"bearerAuth": []
|
|
229
|
+
}
|
|
230
|
+
],
|
|
231
|
+
"parameters": [
|
|
232
|
+
{
|
|
233
|
+
"$ref": "#/components/parameters/AppSlug"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"$ref": "#/components/parameters/Collection"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"$ref": "#/components/parameters/RecordId"
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
"requestBody": {
|
|
243
|
+
"required": true,
|
|
244
|
+
"content": {
|
|
245
|
+
"application/json": {
|
|
246
|
+
"schema": {
|
|
247
|
+
"$ref": "#/components/schemas/RecordWriteRequest"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"responses": {
|
|
253
|
+
"200": {
|
|
254
|
+
"description": "Updated record",
|
|
255
|
+
"content": {
|
|
256
|
+
"application/json": {
|
|
257
|
+
"schema": {
|
|
258
|
+
"$ref": "#/components/schemas/DataRecord"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"404": {
|
|
264
|
+
"$ref": "#/components/responses/NotFound"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
"delete": {
|
|
269
|
+
"tags": ["Records"],
|
|
270
|
+
"operationId": "deleteRecord",
|
|
271
|
+
"summary": "Soft-delete one collection record",
|
|
272
|
+
"security": [
|
|
273
|
+
{
|
|
274
|
+
"bearerAuth": []
|
|
275
|
+
}
|
|
276
|
+
],
|
|
277
|
+
"parameters": [
|
|
278
|
+
{
|
|
279
|
+
"$ref": "#/components/parameters/AppSlug"
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"$ref": "#/components/parameters/Collection"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"$ref": "#/components/parameters/RecordId"
|
|
286
|
+
}
|
|
287
|
+
],
|
|
288
|
+
"responses": {
|
|
289
|
+
"200": {
|
|
290
|
+
"description": "Delete result",
|
|
291
|
+
"content": {
|
|
292
|
+
"application/json": {
|
|
293
|
+
"schema": {
|
|
294
|
+
"$ref": "#/components/schemas/SuccessResponse"
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"404": {
|
|
300
|
+
"$ref": "#/components/responses/NotFound"
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
"/api/platform/apps/{appSlug}/collections/{collection}/value": {
|
|
306
|
+
"get": {
|
|
307
|
+
"tags": ["Values"],
|
|
308
|
+
"operationId": "getCollectionValue",
|
|
309
|
+
"summary": "Read the singleton value for a collection",
|
|
310
|
+
"security": [
|
|
311
|
+
{
|
|
312
|
+
"bearerAuth": []
|
|
313
|
+
}
|
|
314
|
+
],
|
|
315
|
+
"parameters": [
|
|
316
|
+
{
|
|
317
|
+
"$ref": "#/components/parameters/AppSlug"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"$ref": "#/components/parameters/Collection"
|
|
321
|
+
}
|
|
322
|
+
],
|
|
323
|
+
"responses": {
|
|
324
|
+
"200": {
|
|
325
|
+
"description": "Stored JSON value or null",
|
|
326
|
+
"content": {
|
|
327
|
+
"application/json": {
|
|
328
|
+
"schema": {
|
|
329
|
+
"oneOf": [
|
|
330
|
+
{
|
|
331
|
+
"$ref": "#/components/schemas/JsonValue"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"type": "null"
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"401": {
|
|
342
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
"put": {
|
|
347
|
+
"tags": ["Values"],
|
|
348
|
+
"operationId": "setCollectionValue",
|
|
349
|
+
"summary": "Set the singleton value for a collection",
|
|
350
|
+
"security": [
|
|
351
|
+
{
|
|
352
|
+
"bearerAuth": []
|
|
353
|
+
}
|
|
354
|
+
],
|
|
355
|
+
"parameters": [
|
|
356
|
+
{
|
|
357
|
+
"$ref": "#/components/parameters/AppSlug"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"$ref": "#/components/parameters/Collection"
|
|
361
|
+
}
|
|
362
|
+
],
|
|
363
|
+
"requestBody": {
|
|
364
|
+
"required": true,
|
|
365
|
+
"content": {
|
|
366
|
+
"application/json": {
|
|
367
|
+
"schema": {
|
|
368
|
+
"$ref": "#/components/schemas/ValueWriteRequest"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
"responses": {
|
|
374
|
+
"200": {
|
|
375
|
+
"description": "Stored singleton record",
|
|
376
|
+
"content": {
|
|
377
|
+
"application/json": {
|
|
378
|
+
"schema": {
|
|
379
|
+
"$ref": "#/components/schemas/DataRecord"
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
"401": {
|
|
385
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
"components": {
|
|
392
|
+
"securitySchemes": {
|
|
393
|
+
"bearerAuth": {
|
|
394
|
+
"type": "http",
|
|
395
|
+
"scheme": "bearer",
|
|
396
|
+
"bearerFormat": "lokal_app token"
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
"parameters": {
|
|
400
|
+
"AppSlug": {
|
|
401
|
+
"name": "appSlug",
|
|
402
|
+
"in": "path",
|
|
403
|
+
"required": true,
|
|
404
|
+
"schema": {
|
|
405
|
+
"type": "string",
|
|
406
|
+
"pattern": "^[a-z0-9-]+$"
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
"Collection": {
|
|
410
|
+
"name": "collection",
|
|
411
|
+
"in": "path",
|
|
412
|
+
"required": true,
|
|
413
|
+
"schema": {
|
|
414
|
+
"type": "string",
|
|
415
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
"RecordId": {
|
|
419
|
+
"name": "recordId",
|
|
420
|
+
"in": "path",
|
|
421
|
+
"required": true,
|
|
422
|
+
"schema": {
|
|
423
|
+
"type": "string"
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
"responses": {
|
|
428
|
+
"BadRequest": {
|
|
429
|
+
"description": "Invalid request",
|
|
430
|
+
"content": {
|
|
431
|
+
"application/json": {
|
|
432
|
+
"schema": {
|
|
433
|
+
"$ref": "#/components/schemas/ErrorResponse"
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
"Unauthorized": {
|
|
439
|
+
"description": "Missing or invalid credentials"
|
|
440
|
+
},
|
|
441
|
+
"NotFound": {
|
|
442
|
+
"description": "Resource not found"
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
"schemas": {
|
|
446
|
+
"JsonValue": {
|
|
447
|
+
"description": "Any JSON value. App-specific shape is validated by the external app or SDK.",
|
|
448
|
+
"nullable": true
|
|
449
|
+
},
|
|
450
|
+
"LokalAppManifest": {
|
|
451
|
+
"$ref": "./lokal-manifest.schema.json"
|
|
452
|
+
},
|
|
453
|
+
"AuthRequest": {
|
|
454
|
+
"type": "object",
|
|
455
|
+
"required": ["email", "password", "manifest"],
|
|
456
|
+
"properties": {
|
|
457
|
+
"email": {
|
|
458
|
+
"type": "string",
|
|
459
|
+
"format": "email"
|
|
460
|
+
},
|
|
461
|
+
"password": {
|
|
462
|
+
"type": "string"
|
|
463
|
+
},
|
|
464
|
+
"manifest": {
|
|
465
|
+
"$ref": "#/components/schemas/LokalAppManifest"
|
|
466
|
+
},
|
|
467
|
+
"lokalManifest": {
|
|
468
|
+
"$ref": "#/components/schemas/LokalAppManifest"
|
|
469
|
+
},
|
|
470
|
+
"app": {
|
|
471
|
+
"$ref": "#/components/schemas/LokalAppManifest"
|
|
472
|
+
},
|
|
473
|
+
"tokenName": {
|
|
474
|
+
"type": "string"
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
"additionalProperties": true
|
|
478
|
+
},
|
|
479
|
+
"AuthResponse": {
|
|
480
|
+
"type": "object",
|
|
481
|
+
"required": ["apiBase", "app", "user", "token"],
|
|
482
|
+
"properties": {
|
|
483
|
+
"apiBase": {
|
|
484
|
+
"type": "string",
|
|
485
|
+
"format": "uri"
|
|
486
|
+
},
|
|
487
|
+
"app": {
|
|
488
|
+
"$ref": "#/components/schemas/RegisteredApp"
|
|
489
|
+
},
|
|
490
|
+
"user": {
|
|
491
|
+
"$ref": "#/components/schemas/LokalUser"
|
|
492
|
+
},
|
|
493
|
+
"token": {
|
|
494
|
+
"$ref": "#/components/schemas/AppTokenWithSecret"
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
"DiscoveryResponse": {
|
|
499
|
+
"type": "object",
|
|
500
|
+
"required": ["issuer", "apiBase", "features"],
|
|
501
|
+
"properties": {
|
|
502
|
+
"issuer": {
|
|
503
|
+
"type": "string",
|
|
504
|
+
"format": "uri"
|
|
505
|
+
},
|
|
506
|
+
"apiBase": {
|
|
507
|
+
"type": "string",
|
|
508
|
+
"format": "uri"
|
|
509
|
+
},
|
|
510
|
+
"contracts": {
|
|
511
|
+
"type": "object",
|
|
512
|
+
"properties": {
|
|
513
|
+
"openapi": {
|
|
514
|
+
"type": "string"
|
|
515
|
+
},
|
|
516
|
+
"manifestSchema": {
|
|
517
|
+
"type": "string"
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
"additionalProperties": false
|
|
521
|
+
},
|
|
522
|
+
"features": {
|
|
523
|
+
"type": "object",
|
|
524
|
+
"properties": {
|
|
525
|
+
"auth": {
|
|
526
|
+
"type": "array",
|
|
527
|
+
"items": {
|
|
528
|
+
"type": "string"
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
"data": {
|
|
532
|
+
"type": "array",
|
|
533
|
+
"items": {
|
|
534
|
+
"type": "string"
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
"files": {
|
|
538
|
+
"type": "boolean"
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
"additionalProperties": true
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
"additionalProperties": false
|
|
545
|
+
},
|
|
546
|
+
"RegisteredApp": {
|
|
547
|
+
"type": "object",
|
|
548
|
+
"required": ["id", "name", "slug", "clientId", "manifest"],
|
|
549
|
+
"properties": {
|
|
550
|
+
"id": {
|
|
551
|
+
"type": "string"
|
|
552
|
+
},
|
|
553
|
+
"name": {
|
|
554
|
+
"type": "string"
|
|
555
|
+
},
|
|
556
|
+
"slug": {
|
|
557
|
+
"type": "string"
|
|
558
|
+
},
|
|
559
|
+
"clientId": {
|
|
560
|
+
"type": "string"
|
|
561
|
+
},
|
|
562
|
+
"manifest": {
|
|
563
|
+
"$ref": "#/components/schemas/LokalAppManifest"
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
"additionalProperties": true
|
|
567
|
+
},
|
|
568
|
+
"LokalUser": {
|
|
569
|
+
"type": "object",
|
|
570
|
+
"required": ["id", "name", "email", "role"],
|
|
571
|
+
"properties": {
|
|
572
|
+
"id": {
|
|
573
|
+
"type": "string"
|
|
574
|
+
},
|
|
575
|
+
"name": {
|
|
576
|
+
"type": "string"
|
|
577
|
+
},
|
|
578
|
+
"email": {
|
|
579
|
+
"type": "string",
|
|
580
|
+
"format": "email"
|
|
581
|
+
},
|
|
582
|
+
"role": {
|
|
583
|
+
"type": "string"
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
"AppTokenWithSecret": {
|
|
588
|
+
"type": "object",
|
|
589
|
+
"required": ["id", "name", "scopes", "createdAt", "type", "rawToken"],
|
|
590
|
+
"properties": {
|
|
591
|
+
"id": {
|
|
592
|
+
"type": "string"
|
|
593
|
+
},
|
|
594
|
+
"name": {
|
|
595
|
+
"type": "string"
|
|
596
|
+
},
|
|
597
|
+
"scopes": {
|
|
598
|
+
"type": "array",
|
|
599
|
+
"items": {
|
|
600
|
+
"type": "string",
|
|
601
|
+
"enum": ["data:read", "data:write"]
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
"createdAt": {
|
|
605
|
+
"type": "string",
|
|
606
|
+
"format": "date-time"
|
|
607
|
+
},
|
|
608
|
+
"type": {
|
|
609
|
+
"type": "string",
|
|
610
|
+
"const": "Bearer"
|
|
611
|
+
},
|
|
612
|
+
"rawToken": {
|
|
613
|
+
"type": "string",
|
|
614
|
+
"pattern": "^lokal_app_"
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
"DataRecord": {
|
|
619
|
+
"type": "object",
|
|
620
|
+
"required": ["id", "appId", "ownerId", "collection", "value", "version", "createdAt", "updatedAt"],
|
|
621
|
+
"properties": {
|
|
622
|
+
"id": {
|
|
623
|
+
"type": "string"
|
|
624
|
+
},
|
|
625
|
+
"appId": {
|
|
626
|
+
"type": "string"
|
|
627
|
+
},
|
|
628
|
+
"ownerId": {
|
|
629
|
+
"type": "string"
|
|
630
|
+
},
|
|
631
|
+
"collection": {
|
|
632
|
+
"type": "string"
|
|
633
|
+
},
|
|
634
|
+
"key": {
|
|
635
|
+
"type": ["string", "null"]
|
|
636
|
+
},
|
|
637
|
+
"value": {
|
|
638
|
+
"$ref": "#/components/schemas/JsonValue"
|
|
639
|
+
},
|
|
640
|
+
"version": {
|
|
641
|
+
"type": "integer"
|
|
642
|
+
},
|
|
643
|
+
"createdAt": {
|
|
644
|
+
"type": "string",
|
|
645
|
+
"format": "date-time"
|
|
646
|
+
},
|
|
647
|
+
"updatedAt": {
|
|
648
|
+
"type": "string",
|
|
649
|
+
"format": "date-time"
|
|
650
|
+
},
|
|
651
|
+
"deletedAt": {
|
|
652
|
+
"type": ["string", "null"],
|
|
653
|
+
"format": "date-time"
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
"additionalProperties": true
|
|
657
|
+
},
|
|
658
|
+
"RecordWriteRequest": {
|
|
659
|
+
"description": "Either send { value, key? } or send any JSON value directly. The SDK should prefer { value }.",
|
|
660
|
+
"oneOf": [
|
|
661
|
+
{
|
|
662
|
+
"type": "object",
|
|
663
|
+
"properties": {
|
|
664
|
+
"key": {
|
|
665
|
+
"type": ["string", "null"]
|
|
666
|
+
},
|
|
667
|
+
"value": {
|
|
668
|
+
"$ref": "#/components/schemas/JsonValue"
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
"required": ["value"],
|
|
672
|
+
"additionalProperties": true
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"$ref": "#/components/schemas/JsonValue"
|
|
676
|
+
}
|
|
677
|
+
]
|
|
678
|
+
},
|
|
679
|
+
"ValueWriteRequest": {
|
|
680
|
+
"description": "Either send { value } or send any JSON value directly. The SDK should prefer { value }.",
|
|
681
|
+
"oneOf": [
|
|
682
|
+
{
|
|
683
|
+
"type": "object",
|
|
684
|
+
"properties": {
|
|
685
|
+
"value": {
|
|
686
|
+
"$ref": "#/components/schemas/JsonValue"
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
"required": ["value"],
|
|
690
|
+
"additionalProperties": true
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
"$ref": "#/components/schemas/JsonValue"
|
|
694
|
+
}
|
|
695
|
+
]
|
|
696
|
+
},
|
|
697
|
+
"SuccessResponse": {
|
|
698
|
+
"type": "object",
|
|
699
|
+
"required": ["success"],
|
|
700
|
+
"properties": {
|
|
701
|
+
"success": {
|
|
702
|
+
"type": "boolean"
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
"ErrorResponse": {
|
|
707
|
+
"type": "object",
|
|
708
|
+
"required": ["error"],
|
|
709
|
+
"properties": {
|
|
710
|
+
"error": {
|
|
711
|
+
"type": "string"
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,wBAAwB,EAGxB,gBAAgB,EAChB,WAAW,EAGZ,MAAM,SAAS,CAAC;AAMjB,wBAAgB,iBAAiB,CAAC,QAAQ,SAAS,gBAAgB,EACjE,OAAO,EAAE,wBAAwB,CAAC,QAAQ,CAAC,GAC1C,WAAW,CAAC,QAAQ,CAAC,CA4HvB"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBAEX,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;CAM5D"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{status;body;constructor(e,t,n){super(t),this.name=`LokalApiError`,this.status=e,this.body=n}};function t(t){let a=n(t.instanceUrl),o=t.fetch??globalThis.fetch,s=t.token;if(!o)throw Error(`A fetch implementation is required. Pass fetch in createLokalClient options.`);async function c(t,n={}){let{requiresToken:c=!0,headers:l,...u}=n,d=new Headers(l);if(d.set(`Accept`,`application/json`),u.body!==void 0&&!d.has(`Content-Type`)&&d.set(`Content-Type`,`application/json`),c){if(!s)throw Error(`A Lokal app token is required. Sign in first or call setToken().`);d.set(`Authorization`,`Bearer ${s}`)}let f=await o(`${a}${t}`,{...u,headers:d}),p=await r(f);if(!f.ok)throw new e(f.status,i(f,p),p);return p}let l={async signIn(e){let n=await c(`/api/platform/auth`,{method:`POST`,requiresToken:!1,body:JSON.stringify({email:e.email,password:e.password,manifest:t.manifest,...e.tokenName?{tokenName:e.tokenName}:{}})});return s=n.token.rawToken,n}};function u(e){let n=`/api/platform/apps/${encodeURIComponent(t.manifest.slug)}/collections/${encodeURIComponent(e)}`;return{list(e){let t=new URLSearchParams;e?.limit!==void 0&&t.set(`limit`,String(e.limit));let r=t.toString();return c(`${n}/records${r?`?${r}`:``}`)},create(e,t){return c(`${n}/records`,{method:`POST`,body:JSON.stringify({value:e,...t?.key===void 0?{}:{key:t.key}})})},get(e){return c(`${n}/records/${encodeURIComponent(e)}`)},update(e,t,r){return c(`${n}/records/${encodeURIComponent(e)}`,{method:`PATCH`,body:JSON.stringify({value:t,...r&&`key`in r?{key:r.key}:{}})})},delete(e){return c(`${n}/records/${encodeURIComponent(e)}`,{method:`DELETE`})},getValue(){return c(`${n}/value`)},setValue(e){return c(`${n}/value`,{method:`PUT`,body:JSON.stringify({value:e})})}}}return{auth:l,setToken(e){s=e},getToken(){return s},clearToken(){s=void 0},collection:u}}function n(e){let t=e.trim().replace(/\/+$/,``);if(!t)throw Error(`instanceUrl is required.`);return t}async function r(e){let t=await e.text();if(t)try{return JSON.parse(t)}catch{return t}}function i(e,t){if(t&&typeof t==`object`){if(`error`in t&&typeof t.error==`string`)return t.error;if(`message`in t&&typeof t.message==`string`)return t.message}return`Lokal API request failed with status ${e.status}`}function a(e){return e}exports.LokalApiError=e,exports.createLokalClient=t,exports.defineLokalApp=a;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/errors.ts","../src/client.ts","../src/manifest.ts"],"sourcesContent":["export class LokalApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body?: unknown) {\n super(message);\n this.name = 'LokalApiError';\n this.status = status;\n this.body = body;\n }\n}\n","import { LokalApiError } from './errors';\nimport type {\n AuthResponse,\n AuthSignInOptions,\n CreateLokalClientOptions,\n DataRecord,\n JsonValue,\n LokalAppManifest,\n LokalClient,\n LokalCollectionClient,\n SuccessResponse,\n} from './types';\n\ninterface RequestOptions extends RequestInit {\n requiresToken?: boolean;\n}\n\nexport function createLokalClient<Manifest extends LokalAppManifest>(\n options: CreateLokalClientOptions<Manifest>,\n): LokalClient<Manifest> {\n const instanceUrl = normalizeInstanceUrl(options.instanceUrl);\n const fetcher = options.fetch ?? globalThis.fetch;\n let token = options.token;\n\n if (!fetcher) {\n throw new Error('A fetch implementation is required. Pass fetch in createLokalClient options.');\n }\n\n async function request<Result>(path: string, requestOptions: RequestOptions = {}): Promise<Result> {\n const { requiresToken = true, headers, ...init } = requestOptions;\n const requestHeaders = new Headers(headers);\n\n requestHeaders.set('Accept', 'application/json');\n\n if (init.body !== undefined && !requestHeaders.has('Content-Type')) {\n requestHeaders.set('Content-Type', 'application/json');\n }\n\n if (requiresToken) {\n if (!token) {\n throw new Error('A Lokal app token is required. Sign in first or call setToken().');\n }\n requestHeaders.set('Authorization', `Bearer ${token}`);\n }\n\n const response = await fetcher(`${instanceUrl}${path}`, {\n ...init,\n headers: requestHeaders,\n });\n\n const body = await readBody(response);\n\n if (!response.ok) {\n throw new LokalApiError(response.status, getErrorMessage(response, body), body);\n }\n\n return body as Result;\n }\n\n const auth = {\n async signIn(signInOptions: AuthSignInOptions): Promise<AuthResponse<Manifest>> {\n const response = await request<AuthResponse<Manifest>>('/api/platform/auth', {\n method: 'POST',\n requiresToken: false,\n body: JSON.stringify({\n email: signInOptions.email,\n password: signInOptions.password,\n manifest: options.manifest,\n ...(signInOptions.tokenName ? { tokenName: signInOptions.tokenName } : {}),\n }),\n });\n\n token = response.token.rawToken;\n return response;\n },\n };\n\n function collection(name: string): LokalCollectionClient {\n const collectionPath = `/api/platform/apps/${encodeURIComponent(\n options.manifest.slug,\n )}/collections/${encodeURIComponent(name)}`;\n\n return {\n list(listOptions) {\n const search = new URLSearchParams();\n if (listOptions?.limit !== undefined) {\n search.set('limit', String(listOptions.limit));\n }\n\n const query = search.toString();\n return request<DataRecord[]>(`${collectionPath}/records${query ? `?${query}` : ''}`);\n },\n create(value: JsonValue, writeOptions) {\n return request<DataRecord>(`${collectionPath}/records`, {\n method: 'POST',\n body: JSON.stringify({\n value,\n ...(writeOptions?.key !== undefined ? { key: writeOptions.key } : {}),\n }),\n });\n },\n get(recordId: string) {\n return request<DataRecord>(`${collectionPath}/records/${encodeURIComponent(recordId)}`);\n },\n update(recordId: string, value: JsonValue, updateOptions) {\n return request<DataRecord>(`${collectionPath}/records/${encodeURIComponent(recordId)}`, {\n method: 'PATCH',\n body: JSON.stringify({\n value,\n ...(updateOptions && 'key' in updateOptions ? { key: updateOptions.key } : {}),\n }),\n });\n },\n delete(recordId: string) {\n return request<SuccessResponse>(`${collectionPath}/records/${encodeURIComponent(recordId)}`, {\n method: 'DELETE',\n });\n },\n getValue() {\n return request<JsonValue | null>(`${collectionPath}/value`);\n },\n setValue(value: JsonValue) {\n return request<DataRecord>(`${collectionPath}/value`, {\n method: 'PUT',\n body: JSON.stringify({ value }),\n });\n },\n };\n }\n\n return {\n auth,\n setToken(nextToken: string) {\n token = nextToken;\n },\n getToken() {\n return token;\n },\n clearToken() {\n token = undefined;\n },\n collection,\n } as LokalClient<Manifest>;\n}\n\nfunction normalizeInstanceUrl(instanceUrl: string): string {\n const normalized = instanceUrl.trim().replace(/\\/+$/, '');\n if (!normalized) {\n throw new Error('instanceUrl is required.');\n }\n return normalized;\n}\n\nasync function readBody(response: Response): Promise<unknown> {\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction getErrorMessage(response: Response, body: unknown): string {\n if (body && typeof body === 'object') {\n if ('error' in body && typeof body.error === 'string') {\n return body.error;\n }\n if ('message' in body && typeof body.message === 'string') {\n return body.message;\n }\n }\n\n return `Lokal API request failed with status ${response.status}`;\n}\n","import type { LokalAppManifest, LokalCollections } from './types';\n\nexport function defineLokalApp<const Collections extends LokalCollections>(\n manifest: LokalAppManifest<Collections>,\n): LokalAppManifest<Collections> {\n return manifest;\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createLokalClient } from './client';
|
|
2
|
+
export { LokalApiError } from './errors';
|
|
3
|
+
export { defineLokalApp } from './manifest';
|
|
4
|
+
export type { AppTokenWithSecret, AuthResponse, AuthSignInOptions, CreateLokalClientOptions, DataRecord, JsonValue, LokalAppManifest, LokalClient, LokalCollectionClient, LokalCollections, LokalUser, RegisteredApp, SuccessResponse, } from './types';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EACV,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,eAAe,GAChB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
var e = class extends Error {
|
|
3
|
+
status;
|
|
4
|
+
body;
|
|
5
|
+
constructor(e, t, n) {
|
|
6
|
+
super(t), this.name = "LokalApiError", this.status = e, this.body = n;
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/client.ts
|
|
11
|
+
function t(t) {
|
|
12
|
+
let a = n(t.instanceUrl), o = t.fetch ?? globalThis.fetch, s = t.token;
|
|
13
|
+
if (!o) throw Error("A fetch implementation is required. Pass fetch in createLokalClient options.");
|
|
14
|
+
async function c(t, n = {}) {
|
|
15
|
+
let { requiresToken: c = !0, headers: l, ...u } = n, d = new Headers(l);
|
|
16
|
+
if (d.set("Accept", "application/json"), u.body !== void 0 && !d.has("Content-Type") && d.set("Content-Type", "application/json"), c) {
|
|
17
|
+
if (!s) throw Error("A Lokal app token is required. Sign in first or call setToken().");
|
|
18
|
+
d.set("Authorization", `Bearer ${s}`);
|
|
19
|
+
}
|
|
20
|
+
let f = await o(`${a}${t}`, {
|
|
21
|
+
...u,
|
|
22
|
+
headers: d
|
|
23
|
+
}), p = await r(f);
|
|
24
|
+
if (!f.ok) throw new e(f.status, i(f, p), p);
|
|
25
|
+
return p;
|
|
26
|
+
}
|
|
27
|
+
let l = { async signIn(e) {
|
|
28
|
+
let n = await c("/api/platform/auth", {
|
|
29
|
+
method: "POST",
|
|
30
|
+
requiresToken: !1,
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
email: e.email,
|
|
33
|
+
password: e.password,
|
|
34
|
+
manifest: t.manifest,
|
|
35
|
+
...e.tokenName ? { tokenName: e.tokenName } : {}
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
return s = n.token.rawToken, n;
|
|
39
|
+
} };
|
|
40
|
+
function u(e) {
|
|
41
|
+
let n = `/api/platform/apps/${encodeURIComponent(t.manifest.slug)}/collections/${encodeURIComponent(e)}`;
|
|
42
|
+
return {
|
|
43
|
+
list(e) {
|
|
44
|
+
let t = new URLSearchParams();
|
|
45
|
+
e?.limit !== void 0 && t.set("limit", String(e.limit));
|
|
46
|
+
let r = t.toString();
|
|
47
|
+
return c(`${n}/records${r ? `?${r}` : ""}`);
|
|
48
|
+
},
|
|
49
|
+
create(e, t) {
|
|
50
|
+
return c(`${n}/records`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
value: e,
|
|
54
|
+
...t?.key === void 0 ? {} : { key: t.key }
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
get(e) {
|
|
59
|
+
return c(`${n}/records/${encodeURIComponent(e)}`);
|
|
60
|
+
},
|
|
61
|
+
update(e, t, r) {
|
|
62
|
+
return c(`${n}/records/${encodeURIComponent(e)}`, {
|
|
63
|
+
method: "PATCH",
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
value: t,
|
|
66
|
+
...r && "key" in r ? { key: r.key } : {}
|
|
67
|
+
})
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
delete(e) {
|
|
71
|
+
return c(`${n}/records/${encodeURIComponent(e)}`, { method: "DELETE" });
|
|
72
|
+
},
|
|
73
|
+
getValue() {
|
|
74
|
+
return c(`${n}/value`);
|
|
75
|
+
},
|
|
76
|
+
setValue(e) {
|
|
77
|
+
return c(`${n}/value`, {
|
|
78
|
+
method: "PUT",
|
|
79
|
+
body: JSON.stringify({ value: e })
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
auth: l,
|
|
86
|
+
setToken(e) {
|
|
87
|
+
s = e;
|
|
88
|
+
},
|
|
89
|
+
getToken() {
|
|
90
|
+
return s;
|
|
91
|
+
},
|
|
92
|
+
clearToken() {
|
|
93
|
+
s = void 0;
|
|
94
|
+
},
|
|
95
|
+
collection: u
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function n(e) {
|
|
99
|
+
let t = e.trim().replace(/\/+$/, "");
|
|
100
|
+
if (!t) throw Error("instanceUrl is required.");
|
|
101
|
+
return t;
|
|
102
|
+
}
|
|
103
|
+
async function r(e) {
|
|
104
|
+
let t = await e.text();
|
|
105
|
+
if (t) try {
|
|
106
|
+
return JSON.parse(t);
|
|
107
|
+
} catch {
|
|
108
|
+
return t;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function i(e, t) {
|
|
112
|
+
if (t && typeof t == "object") {
|
|
113
|
+
if ("error" in t && typeof t.error == "string") return t.error;
|
|
114
|
+
if ("message" in t && typeof t.message == "string") return t.message;
|
|
115
|
+
}
|
|
116
|
+
return `Lokal API request failed with status ${e.status}`;
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/manifest.ts
|
|
120
|
+
function a(e) {
|
|
121
|
+
return e;
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { e as LokalApiError, t as createLokalClient, a as defineLokalApp };
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/client.ts","../src/manifest.ts"],"sourcesContent":["export class LokalApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body?: unknown) {\n super(message);\n this.name = 'LokalApiError';\n this.status = status;\n this.body = body;\n }\n}\n","import { LokalApiError } from './errors';\nimport type {\n AuthResponse,\n AuthSignInOptions,\n CreateLokalClientOptions,\n DataRecord,\n JsonValue,\n LokalAppManifest,\n LokalClient,\n LokalCollectionClient,\n SuccessResponse,\n} from './types';\n\ninterface RequestOptions extends RequestInit {\n requiresToken?: boolean;\n}\n\nexport function createLokalClient<Manifest extends LokalAppManifest>(\n options: CreateLokalClientOptions<Manifest>,\n): LokalClient<Manifest> {\n const instanceUrl = normalizeInstanceUrl(options.instanceUrl);\n const fetcher = options.fetch ?? globalThis.fetch;\n let token = options.token;\n\n if (!fetcher) {\n throw new Error('A fetch implementation is required. Pass fetch in createLokalClient options.');\n }\n\n async function request<Result>(path: string, requestOptions: RequestOptions = {}): Promise<Result> {\n const { requiresToken = true, headers, ...init } = requestOptions;\n const requestHeaders = new Headers(headers);\n\n requestHeaders.set('Accept', 'application/json');\n\n if (init.body !== undefined && !requestHeaders.has('Content-Type')) {\n requestHeaders.set('Content-Type', 'application/json');\n }\n\n if (requiresToken) {\n if (!token) {\n throw new Error('A Lokal app token is required. Sign in first or call setToken().');\n }\n requestHeaders.set('Authorization', `Bearer ${token}`);\n }\n\n const response = await fetcher(`${instanceUrl}${path}`, {\n ...init,\n headers: requestHeaders,\n });\n\n const body = await readBody(response);\n\n if (!response.ok) {\n throw new LokalApiError(response.status, getErrorMessage(response, body), body);\n }\n\n return body as Result;\n }\n\n const auth = {\n async signIn(signInOptions: AuthSignInOptions): Promise<AuthResponse<Manifest>> {\n const response = await request<AuthResponse<Manifest>>('/api/platform/auth', {\n method: 'POST',\n requiresToken: false,\n body: JSON.stringify({\n email: signInOptions.email,\n password: signInOptions.password,\n manifest: options.manifest,\n ...(signInOptions.tokenName ? { tokenName: signInOptions.tokenName } : {}),\n }),\n });\n\n token = response.token.rawToken;\n return response;\n },\n };\n\n function collection(name: string): LokalCollectionClient {\n const collectionPath = `/api/platform/apps/${encodeURIComponent(\n options.manifest.slug,\n )}/collections/${encodeURIComponent(name)}`;\n\n return {\n list(listOptions) {\n const search = new URLSearchParams();\n if (listOptions?.limit !== undefined) {\n search.set('limit', String(listOptions.limit));\n }\n\n const query = search.toString();\n return request<DataRecord[]>(`${collectionPath}/records${query ? `?${query}` : ''}`);\n },\n create(value: JsonValue, writeOptions) {\n return request<DataRecord>(`${collectionPath}/records`, {\n method: 'POST',\n body: JSON.stringify({\n value,\n ...(writeOptions?.key !== undefined ? { key: writeOptions.key } : {}),\n }),\n });\n },\n get(recordId: string) {\n return request<DataRecord>(`${collectionPath}/records/${encodeURIComponent(recordId)}`);\n },\n update(recordId: string, value: JsonValue, updateOptions) {\n return request<DataRecord>(`${collectionPath}/records/${encodeURIComponent(recordId)}`, {\n method: 'PATCH',\n body: JSON.stringify({\n value,\n ...(updateOptions && 'key' in updateOptions ? { key: updateOptions.key } : {}),\n }),\n });\n },\n delete(recordId: string) {\n return request<SuccessResponse>(`${collectionPath}/records/${encodeURIComponent(recordId)}`, {\n method: 'DELETE',\n });\n },\n getValue() {\n return request<JsonValue | null>(`${collectionPath}/value`);\n },\n setValue(value: JsonValue) {\n return request<DataRecord>(`${collectionPath}/value`, {\n method: 'PUT',\n body: JSON.stringify({ value }),\n });\n },\n };\n }\n\n return {\n auth,\n setToken(nextToken: string) {\n token = nextToken;\n },\n getToken() {\n return token;\n },\n clearToken() {\n token = undefined;\n },\n collection,\n } as LokalClient<Manifest>;\n}\n\nfunction normalizeInstanceUrl(instanceUrl: string): string {\n const normalized = instanceUrl.trim().replace(/\\/+$/, '');\n if (!normalized) {\n throw new Error('instanceUrl is required.');\n }\n return normalized;\n}\n\nasync function readBody(response: Response): Promise<unknown> {\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction getErrorMessage(response: Response, body: unknown): string {\n if (body && typeof body === 'object') {\n if ('error' in body && typeof body.error === 'string') {\n return body.error;\n }\n if ('message' in body && typeof body.message === 'string') {\n return body.message;\n }\n }\n\n return `Lokal API request failed with status ${response.status}`;\n}\n","import type { LokalAppManifest, LokalCollections } from './types';\n\nexport function defineLokalApp<const Collections extends LokalCollections>(\n manifest: LokalAppManifest<Collections>,\n): LokalAppManifest<Collections> {\n return manifest;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAElE,wBAAgB,cAAc,CAAC,KAAK,CAAC,WAAW,SAAS,gBAAgB,EACvE,QAAQ,EAAE,gBAAgB,CAAC,WAAW,CAAC,GACtC,gBAAgB,CAAC,WAAW,CAAC,CAE/B"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
2
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
export type LokalCollections = Record<string, unknown>;
|
|
6
|
+
export interface LokalAppManifest<Collections extends LokalCollections = LokalCollections> {
|
|
7
|
+
name?: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
developerName?: string;
|
|
11
|
+
collections: Collections;
|
|
12
|
+
}
|
|
13
|
+
export interface LokalUser {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
email: string;
|
|
17
|
+
role: string;
|
|
18
|
+
}
|
|
19
|
+
export interface RegisteredApp<Manifest extends LokalAppManifest = LokalAppManifest> {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
slug: string;
|
|
23
|
+
clientId: string;
|
|
24
|
+
manifest: Manifest;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
export interface AppTokenWithSecret {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
scopes: Array<'data:read' | 'data:write'>;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
type: 'Bearer';
|
|
33
|
+
rawToken: string;
|
|
34
|
+
}
|
|
35
|
+
export interface AuthResponse<Manifest extends LokalAppManifest = LokalAppManifest> {
|
|
36
|
+
apiBase: string;
|
|
37
|
+
app: RegisteredApp<Manifest>;
|
|
38
|
+
user: LokalUser;
|
|
39
|
+
token: AppTokenWithSecret;
|
|
40
|
+
}
|
|
41
|
+
export interface AuthSignInOptions {
|
|
42
|
+
email: string;
|
|
43
|
+
password: string;
|
|
44
|
+
tokenName?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface DataRecord<Value extends JsonValue = JsonValue> {
|
|
47
|
+
id: string;
|
|
48
|
+
appId: string;
|
|
49
|
+
ownerId: string;
|
|
50
|
+
collection: string;
|
|
51
|
+
value: Value;
|
|
52
|
+
version: number;
|
|
53
|
+
createdAt: string;
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
deletedAt?: string | null;
|
|
56
|
+
key?: string | null;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
export interface SuccessResponse {
|
|
60
|
+
success: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface ListOptions {
|
|
63
|
+
limit?: number;
|
|
64
|
+
}
|
|
65
|
+
export interface WriteOptions {
|
|
66
|
+
key?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface UpdateOptions {
|
|
69
|
+
key?: string | null;
|
|
70
|
+
}
|
|
71
|
+
export interface LokalCollectionClient {
|
|
72
|
+
list(options?: ListOptions): Promise<DataRecord[]>;
|
|
73
|
+
create(value: JsonValue, options?: WriteOptions): Promise<DataRecord>;
|
|
74
|
+
get(recordId: string): Promise<DataRecord>;
|
|
75
|
+
update(recordId: string, value: JsonValue, options?: UpdateOptions): Promise<DataRecord>;
|
|
76
|
+
delete(recordId: string): Promise<SuccessResponse>;
|
|
77
|
+
getValue(): Promise<JsonValue | null>;
|
|
78
|
+
setValue(value: JsonValue): Promise<DataRecord>;
|
|
79
|
+
}
|
|
80
|
+
export type CollectionName<Manifest extends LokalAppManifest> = Extract<keyof Manifest['collections'], string>;
|
|
81
|
+
export interface LokalClient<Manifest extends LokalAppManifest = LokalAppManifest> {
|
|
82
|
+
auth: {
|
|
83
|
+
signIn(options: AuthSignInOptions): Promise<AuthResponse<Manifest>>;
|
|
84
|
+
};
|
|
85
|
+
setToken(token: string): void;
|
|
86
|
+
getToken(): string | undefined;
|
|
87
|
+
clearToken(): void;
|
|
88
|
+
collection(name: CollectionName<Manifest>): LokalCollectionClient;
|
|
89
|
+
}
|
|
90
|
+
export interface CreateLokalClientOptions<Manifest extends LokalAppManifest = LokalAppManifest> {
|
|
91
|
+
instanceUrl: string;
|
|
92
|
+
manifest: Manifest;
|
|
93
|
+
token?: string;
|
|
94
|
+
fetch?: typeof fetch;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lokal-server/ts-sdk",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "TypeScript SDK for Lokal external apps.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"contracts",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"dev": "vite build --watch",
|
|
28
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"lint": "eslint .",
|
|
32
|
+
"check": "npm run typecheck && npm run lint && npm run test",
|
|
33
|
+
"contracts:update": "node scripts/update-contracts.mjs"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@eslint/js": "^10.0.1",
|
|
37
|
+
"@types/node": "^26.0.0",
|
|
38
|
+
"eslint": "^10.5.0",
|
|
39
|
+
"globals": "^17.7.0",
|
|
40
|
+
"typescript": "^6.0.3",
|
|
41
|
+
"typescript-eslint": "^8.62.0",
|
|
42
|
+
"vite": "^8.1.0",
|
|
43
|
+
"vitest": "^4.1.9"
|
|
44
|
+
}
|
|
45
|
+
}
|