@makemind/sdk 0.1.0 → 0.2.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.
- package/CHANGELOG.md +59 -0
- package/README.md +11 -7
- package/dist/index.d.mts +98 -92
- package/dist/index.d.ts +98 -92
- package/dist/index.js +95 -71
- package/dist/index.mjs +95 -71
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,65 @@ All notable changes to `@makemind/sdk` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 0.2.0 — 2026-10-09
|
|
9
|
+
|
|
10
|
+
### Changed — breaking
|
|
11
|
+
|
|
12
|
+
- Pages carry `dsl` (an MCP UI DSL page) in place of `components`, `layout`,
|
|
13
|
+
`variables`, `dataSources` and `events`; apps carry `definition` (theme,
|
|
14
|
+
navigation, languages) and `rollout`. `ComponentNode` is replaced by
|
|
15
|
+
`DslNode` / `DslPage`.
|
|
16
|
+
- `apps.generateUI` returns `{ content }` — a DSL widget subtree.
|
|
17
|
+
`apps.suggestActions(appId, widgetType?)` returns DSL actions.
|
|
18
|
+
- `apps.publish(appId, changelog?, { rolloutPercent })` publishes to a share of
|
|
19
|
+
visitors.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- `recordSaleWithShares({ delivered })` — what went out with the sale (name,
|
|
24
|
+
quantity, unit, item id), recorded beside the money.
|
|
25
|
+
- `reverseSaleWithShares({ returnedQuantity })` — units that came back. Without
|
|
26
|
+
it a partial refund is a price adjustment and the refund that completes the
|
|
27
|
+
sale returns every unit still out.
|
|
28
|
+
|
|
29
|
+
### Removed (breaking)
|
|
30
|
+
|
|
31
|
+
- `signUp`, `sendPasswordResetEmail`, `confirmPasswordReset` and `verifyEmail`.
|
|
32
|
+
The routes behind them were removed from the platform on 2026-09-17 and
|
|
33
|
+
answer `404`, so every call could only fail. A published client that offers
|
|
34
|
+
an operation the server does not have is a promise its caller discovers is
|
|
35
|
+
false at runtime.
|
|
36
|
+
|
|
37
|
+
What replaces them: a platform account exists once its person signs in at the
|
|
38
|
+
tenant portal; a tenant's own end users register in that tenant's pool
|
|
39
|
+
(`endusers-selfRegisterEndUser`). Sign-in (`signInWithEmail`,
|
|
40
|
+
`signInWithOAuthToken`) and `updatePassword` for a signed-in person are
|
|
41
|
+
unchanged.
|
|
42
|
+
|
|
43
|
+
## 0.1.1 — 2026-09-12
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- The `data` service spanned two stores. `create`, `get`, `update` and `delete` went through the
|
|
48
|
+
REST data door — a separate tree that admits only a fixed set of collection names — while
|
|
49
|
+
`query`, the batch methods, `aggregate` and `search` address the document platform. A document
|
|
50
|
+
created with `create` was invisible to `query` and refused by `batchDelete`, and a collection
|
|
51
|
+
of your own naming was refused by `create` outright. All four now address the document
|
|
52
|
+
platform: ids are `DOC_…`, `get` answers `{ id, data }`, and a collection is taken as named.
|
|
53
|
+
- `refreshAuthToken()` signed the user out — and revoked their refresh tokens server-side — when
|
|
54
|
+
the gateway answered that no refresh token was sent. That is the answer every client without the
|
|
55
|
+
refresh cookie gets (a server-side process, for one), and its id token is still valid. It now
|
|
56
|
+
keeps the session and raises `MAKEMINDError('AUTH_REFRESH_UNAVAILABLE')`; a refresh token the
|
|
57
|
+
server refused still signs out. `createTenant` and `switchTenant` refresh internally, so a
|
|
58
|
+
server-side consumer signs in again after them to pick up the new claims.
|
|
59
|
+
|
|
60
|
+
- `batchCreate`, `batchUpdate` and `batchDelete` reported a batch the backend partly refused as
|
|
61
|
+
written. `dataBatchWrite` answers per item, and an item it will not perform comes back
|
|
62
|
+
`status: 'denied'` with a reason (`DATA_ALREADY_EXISTS`, `DATA_WRITE_ONCE_VIOLATION`,
|
|
63
|
+
`AUTH_ACCESS_DENIED`) while the rest commit. A denied item now raises
|
|
64
|
+
`MAKEMINDError('BATCH_PARTIALLY_DENIED')`, whose `details.results` lists every item and
|
|
65
|
+
`details.denied` the refused ones.
|
|
66
|
+
|
|
8
67
|
## 0.1.0
|
|
9
68
|
|
|
10
69
|
First release. The public surface is not frozen: while this package is 0.x, a minor bump may change it.
|
package/README.md
CHANGED
|
@@ -61,14 +61,11 @@ initializeMAKEMIND({
|
|
|
61
61
|
```typescript
|
|
62
62
|
const makemind = getMAKEMIND();
|
|
63
63
|
|
|
64
|
-
// Sign in with email + password
|
|
64
|
+
// Sign in with email + password. There is no sign-up here: a platform account
|
|
65
|
+
// exists once its person signs in at the tenant portal, and a tenant's own end
|
|
66
|
+
// users register in that tenant's pool (`endusers-selfRegisterEndUser`).
|
|
65
67
|
const user = await makemind.auth.signInWithEmail(email, password);
|
|
66
68
|
|
|
67
|
-
// Sign up a new user
|
|
68
|
-
const newUser = await makemind.auth.signUp(email, password, {
|
|
69
|
-
displayName: 'Jane Doe',
|
|
70
|
-
});
|
|
71
|
-
|
|
72
69
|
// Listen to auth state changes
|
|
73
70
|
const unsubscribe = makemind.auth.onAuthStateChanged((user) => {
|
|
74
71
|
if (user) {
|
|
@@ -88,6 +85,12 @@ const memberships = await makemind.auth.getUserMemberships();
|
|
|
88
85
|
await makemind.auth.switchTenant('new-tenant-id');
|
|
89
86
|
```
|
|
90
87
|
|
|
88
|
+
The refresh token is an HttpOnly cookie, so only a browser can refresh a session. A
|
|
89
|
+
server-side process keeps its id token until it expires; `refreshAuthToken()` there raises
|
|
90
|
+
`AUTH_REFRESH_UNAVAILABLE` and leaves the session intact. `createTenant` and `switchTenant`
|
|
91
|
+
refresh internally, so on a server sign in again after them to pick up the new claims — or use
|
|
92
|
+
an API key, which is the credential made for machine callers.
|
|
93
|
+
|
|
91
94
|
## Data Operations
|
|
92
95
|
|
|
93
96
|
```typescript
|
|
@@ -110,7 +113,8 @@ const product = await makemind.data.get({
|
|
|
110
113
|
documentId: 'product-id',
|
|
111
114
|
});
|
|
112
115
|
|
|
113
|
-
// Create document
|
|
116
|
+
// Create document — every data method addresses the same document store,
|
|
117
|
+
// under whatever collection name you give it; ids are DOC_-prefixed
|
|
114
118
|
const result = await makemind.data.create({
|
|
115
119
|
collection: 'plm_products',
|
|
116
120
|
data: {
|
package/dist/index.d.mts
CHANGED
|
@@ -224,6 +224,19 @@ declare class MAKEMINDSettlement {
|
|
|
224
224
|
description?: string;
|
|
225
225
|
metadata?: Record<string, unknown>;
|
|
226
226
|
}>;
|
|
227
|
+
/**
|
|
228
|
+
* What went out with the sale, recorded beside the money — at a price of
|
|
229
|
+
* nought too. One line per sale, at the gross.
|
|
230
|
+
*/
|
|
231
|
+
delivered?: {
|
|
232
|
+
name: string;
|
|
233
|
+
/** Defaults to 1. */
|
|
234
|
+
quantity?: number;
|
|
235
|
+
unit?: string;
|
|
236
|
+
productId?: string;
|
|
237
|
+
itemRef?: string;
|
|
238
|
+
subscriptionId?: string;
|
|
239
|
+
};
|
|
227
240
|
}): Promise<{
|
|
228
241
|
transactionId: string;
|
|
229
242
|
shareTransactionIds: (string | null)[];
|
|
@@ -243,6 +256,13 @@ declare class MAKEMINDSettlement {
|
|
|
243
256
|
reverseSaleWithShares(options: {
|
|
244
257
|
transactionId: string;
|
|
245
258
|
amount?: number;
|
|
259
|
+
/**
|
|
260
|
+
* Units that came back, when you know. Without it the ledger never guesses
|
|
261
|
+
* units from money: a refund short of the whole is a price adjustment (the
|
|
262
|
+
* thing stays delivered), and the refund that completes it returns every
|
|
263
|
+
* unit still out.
|
|
264
|
+
*/
|
|
265
|
+
returnedQuantity?: number;
|
|
246
266
|
reason?: string;
|
|
247
267
|
idempotencyKey: string;
|
|
248
268
|
}): Promise<{
|
|
@@ -583,12 +603,7 @@ declare class MAKEMINDAuth {
|
|
|
583
603
|
loadMemberships(): Promise<TenantMembership[]>;
|
|
584
604
|
signInWithEmail(email: string, password: string): Promise<AuthUser>;
|
|
585
605
|
signInWithEmailAndPassword(email: string, password: string): Promise<AuthUser>;
|
|
586
|
-
signUp(email: string, password: string, options?: {
|
|
587
|
-
displayName?: string;
|
|
588
|
-
phoneNumber?: string;
|
|
589
|
-
}): Promise<AuthUser>;
|
|
590
606
|
signOut(): Promise<void>;
|
|
591
|
-
sendPasswordResetEmail(email: string): Promise<void>;
|
|
592
607
|
onAuthStateChanged(callback: AuthStateCallback): () => void;
|
|
593
608
|
private notifyAuthStateCallbacks;
|
|
594
609
|
refreshAuthToken(): Promise<void>;
|
|
@@ -617,14 +632,6 @@ declare class MAKEMINDAuth {
|
|
|
617
632
|
* Backend dispatch 'resendVerificationEmail' — uid resolved from auth context, no payload needed.
|
|
618
633
|
*/
|
|
619
634
|
sendEmailVerification(): Promise<void>;
|
|
620
|
-
/**
|
|
621
|
-
* Verify email with code
|
|
622
|
-
*/
|
|
623
|
-
verifyEmail(code: string): Promise<void>;
|
|
624
|
-
/**
|
|
625
|
-
* Confirm password reset with code
|
|
626
|
-
*/
|
|
627
|
-
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
628
635
|
/**
|
|
629
636
|
* OAuth redirect-based sign-in is NOT supported by this backend.
|
|
630
637
|
* The backend has no /oauth/initiate or /oauth/callback routes.
|
|
@@ -707,6 +714,17 @@ declare class MAKEMINDData {
|
|
|
707
714
|
data: Record<string, unknown>;
|
|
708
715
|
documentId?: string;
|
|
709
716
|
}>): Promise<Array<DocumentResult<T>>>;
|
|
717
|
+
/**
|
|
718
|
+
* Send a batch and refuse to report a partial one as whole.
|
|
719
|
+
*
|
|
720
|
+
* The backend answers per item: an item it would not perform comes back
|
|
721
|
+
* `status: 'denied'` with the reason (`DATA_ALREADY_EXISTS` for a create over
|
|
722
|
+
* an id that exists, `DATA_WRITE_ONCE_VIOLATION`, `AUTH_ACCESS_DENIED`) while
|
|
723
|
+
* the others commit. Reading only the ids told the caller every item was
|
|
724
|
+
* written. A denied item now raises `BATCH_PARTIALLY_DENIED`, and the error
|
|
725
|
+
* carries every item's result so the caller can see which ones did commit.
|
|
726
|
+
*/
|
|
727
|
+
private batchWrite;
|
|
710
728
|
batchUpdate(collection: string, updates: Array<{
|
|
711
729
|
documentId: string;
|
|
712
730
|
data: Record<string, unknown>;
|
|
@@ -2099,7 +2117,6 @@ declare class MAKEMINDSearch {
|
|
|
2099
2117
|
*/
|
|
2100
2118
|
|
|
2101
2119
|
type AppStatus = 'draft' | 'published' | 'archived';
|
|
2102
|
-
type LayoutType = 'blank' | 'sidebar' | 'topnav' | 'dashboard' | 'centered';
|
|
2103
2120
|
type SSLStatus = 'pending' | 'provisioning' | 'active' | 'expired' | 'failed';
|
|
2104
2121
|
type CustomDomainStatus = 'pending' | 'verifying' | 'active' | 'failed';
|
|
2105
2122
|
/** Where a hostname's traffic is routed once it reaches the domain-serving edge. */
|
|
@@ -2208,18 +2225,17 @@ interface App {
|
|
|
2208
2225
|
publishedVersion: number | null;
|
|
2209
2226
|
domain: DomainConfig;
|
|
2210
2227
|
settings: AppSettings;
|
|
2228
|
+
definition?: AppDefinition;
|
|
2229
|
+
/** A version served to a share of visitors beside the published one. */
|
|
2230
|
+
rollout?: {
|
|
2231
|
+
version: number;
|
|
2232
|
+
percent: number;
|
|
2233
|
+
};
|
|
2211
2234
|
createdAt: string;
|
|
2212
2235
|
createdBy: string;
|
|
2213
2236
|
updatedAt: string;
|
|
2214
2237
|
updatedBy: string;
|
|
2215
2238
|
}
|
|
2216
|
-
interface LayoutConfig {
|
|
2217
|
-
sidebarWidth?: number;
|
|
2218
|
-
sidebarCollapsible?: boolean;
|
|
2219
|
-
headerHeight?: number;
|
|
2220
|
-
footerHeight?: number;
|
|
2221
|
-
contentMaxWidth?: number;
|
|
2222
|
-
}
|
|
2223
2239
|
interface PageSEO {
|
|
2224
2240
|
title: string;
|
|
2225
2241
|
description: string;
|
|
@@ -2227,61 +2243,42 @@ interface PageSEO {
|
|
|
2227
2243
|
noIndex?: boolean;
|
|
2228
2244
|
}
|
|
2229
2245
|
/**
|
|
2230
|
-
* A
|
|
2231
|
-
*
|
|
2232
|
-
* This described nine of the tree's fields while the editor writes fifteen:
|
|
2233
|
-
* `dataBinding`, `validation`, `a11y` and `conditions.className` were absent
|
|
2234
|
-
* entirely. TypeScript does not delete what it cannot see, so a round trip
|
|
2235
|
-
* through `getPage`/`updatePage` kept them — but a consumer BUILDING a node
|
|
2236
|
-
* from this type had no way to express a binding or a validation rule, and
|
|
2237
|
-
* one rebuilding a node field-by-field dropped them silently.
|
|
2246
|
+
* A widget of a page document — MCP UI DSL 1.4 (app builder DDD 08 §2).
|
|
2238
2247
|
*
|
|
2239
|
-
* `type`
|
|
2240
|
-
*
|
|
2241
|
-
*
|
|
2242
|
-
*
|
|
2243
|
-
* Kept in step with `@makemind/app-builder-ui`'s `ComponentNode` by
|
|
2244
|
-
* `src/__tests__/component-node-parity.test.ts`.
|
|
2248
|
+
* `type` is a widget type the spec defines; every node the builder holds
|
|
2249
|
+
* carries `x-id`. The other properties are the widget's own, as the spec
|
|
2250
|
+
* declares them, so they are not enumerated here.
|
|
2245
2251
|
*/
|
|
2246
|
-
interface
|
|
2247
|
-
id: string;
|
|
2252
|
+
interface DslNode {
|
|
2248
2253
|
type: string;
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2254
|
+
'x-id'?: string;
|
|
2255
|
+
[prop: string]: unknown;
|
|
2256
|
+
}
|
|
2257
|
+
/** A page document (page.schema). */
|
|
2258
|
+
interface DslPage {
|
|
2259
|
+
type: 'page';
|
|
2260
|
+
title?: string;
|
|
2261
|
+
content: DslNode;
|
|
2262
|
+
state?: {
|
|
2263
|
+
initial?: Record<string, unknown>;
|
|
2257
2264
|
};
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2265
|
+
lifecycle?: Record<string, unknown>;
|
|
2266
|
+
[prop: string]: unknown;
|
|
2267
|
+
}
|
|
2268
|
+
/** Theme, navigation, languages and app state — the DSL around the pages. */
|
|
2269
|
+
interface AppDefinition {
|
|
2270
|
+
title?: string;
|
|
2271
|
+
theme?: Record<string, unknown>;
|
|
2272
|
+
navigation?: {
|
|
2273
|
+
type: 'drawer' | 'bottomBar' | 'rail' | 'tabs';
|
|
2274
|
+
items?: unknown[];
|
|
2275
|
+
[key: string]: unknown;
|
|
2263
2276
|
};
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
message: string;
|
|
2268
|
-
}>;
|
|
2269
|
-
a11y?: {
|
|
2270
|
-
label?: string;
|
|
2271
|
-
description?: string;
|
|
2272
|
-
role?: string;
|
|
2277
|
+
i18n?: Record<string, unknown>;
|
|
2278
|
+
state?: {
|
|
2279
|
+
initial?: Record<string, unknown>;
|
|
2273
2280
|
};
|
|
2274
|
-
|
|
2275
|
-
interface DataSource {
|
|
2276
|
-
id: string;
|
|
2277
|
-
name: string;
|
|
2278
|
-
type: 'query' | 'document' | 'api' | 'flow' | 'variable' | 'realtime';
|
|
2279
|
-
config: Record<string, unknown>;
|
|
2280
|
-
}
|
|
2281
|
-
interface PageVariable {
|
|
2282
|
-
name: string;
|
|
2283
|
-
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
2284
|
-
defaultValue: unknown;
|
|
2281
|
+
[key: string]: unknown;
|
|
2285
2282
|
}
|
|
2286
2283
|
interface Page {
|
|
2287
2284
|
id: string;
|
|
@@ -2293,13 +2290,8 @@ interface Page {
|
|
|
2293
2290
|
isHomePage: boolean;
|
|
2294
2291
|
isPublic: boolean;
|
|
2295
2292
|
accessRoles: string[];
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
config: LayoutConfig;
|
|
2299
|
-
};
|
|
2300
|
-
components: ComponentNode[];
|
|
2301
|
-
dataSources: DataSource[];
|
|
2302
|
-
variables: PageVariable[];
|
|
2293
|
+
/** The page document the runner draws. */
|
|
2294
|
+
dsl: DslPage;
|
|
2303
2295
|
seo: PageSEO;
|
|
2304
2296
|
order: number;
|
|
2305
2297
|
createdAt: string;
|
|
@@ -2373,6 +2365,13 @@ interface UpdateAppParams {
|
|
|
2373
2365
|
icon?: string;
|
|
2374
2366
|
status?: AppStatus;
|
|
2375
2367
|
settings?: AppSettings;
|
|
2368
|
+
/** Replaces the app definition as a whole. */
|
|
2369
|
+
definition?: AppDefinition;
|
|
2370
|
+
/** Adjust a rollout; `percent: 100` promotes it, `null` ends it. */
|
|
2371
|
+
rollout?: {
|
|
2372
|
+
version: number;
|
|
2373
|
+
percent: number;
|
|
2374
|
+
} | null;
|
|
2376
2375
|
}
|
|
2377
2376
|
interface ListAppsParams {
|
|
2378
2377
|
status?: AppStatus;
|
|
@@ -2386,10 +2385,9 @@ interface CreatePageParams {
|
|
|
2386
2385
|
description?: string;
|
|
2387
2386
|
isHomePage?: boolean;
|
|
2388
2387
|
isPublic?: boolean;
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
};
|
|
2388
|
+
/** The page document; an empty column when absent. */
|
|
2389
|
+
dsl?: DslPage;
|
|
2390
|
+
seo?: Partial<PageSEO>;
|
|
2393
2391
|
}
|
|
2394
2392
|
interface UpdatePageParams {
|
|
2395
2393
|
name?: string;
|
|
@@ -2399,13 +2397,8 @@ interface UpdatePageParams {
|
|
|
2399
2397
|
isHomePage?: boolean;
|
|
2400
2398
|
isPublic?: boolean;
|
|
2401
2399
|
accessRoles?: string[];
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
config?: LayoutConfig;
|
|
2405
|
-
};
|
|
2406
|
-
components?: ComponentNode[];
|
|
2407
|
-
dataSources?: DataSource[];
|
|
2408
|
-
variables?: PageVariable[];
|
|
2400
|
+
/** Replaces the page document as a whole. */
|
|
2401
|
+
dsl?: DslPage;
|
|
2409
2402
|
seo?: PageSEO;
|
|
2410
2403
|
}
|
|
2411
2404
|
interface MAKEMINDAppsConfig extends MAKEMINDConfig {
|
|
@@ -2443,7 +2436,13 @@ declare class MAKEMINDApps {
|
|
|
2443
2436
|
duplicatePage(appId: string, pageId: string, newName?: string, newPath?: string): Promise<{
|
|
2444
2437
|
pageId: string;
|
|
2445
2438
|
}>;
|
|
2446
|
-
|
|
2439
|
+
/**
|
|
2440
|
+
* Cut a version and publish it — to everyone, or with `rolloutPercent`
|
|
2441
|
+
* (1–99) to that share of visitors beside the published version.
|
|
2442
|
+
*/
|
|
2443
|
+
publish(appId: string, changelog?: string, options?: {
|
|
2444
|
+
rolloutPercent?: number;
|
|
2445
|
+
}): Promise<{
|
|
2447
2446
|
success: boolean;
|
|
2448
2447
|
version: number;
|
|
2449
2448
|
publishedAt: string;
|
|
@@ -2661,11 +2660,16 @@ declare class MAKEMINDApps {
|
|
|
2661
2660
|
startDate?: string;
|
|
2662
2661
|
endDate?: string;
|
|
2663
2662
|
}): Promise<AppAnalytics>;
|
|
2663
|
+
/** A widget subtree in DSL from a description (app builder DDD 08 §9). */
|
|
2664
2664
|
generateUI(prompt: string, context?: Record<string, unknown>, appId?: string): Promise<{
|
|
2665
|
-
|
|
2665
|
+
content: DslNode;
|
|
2666
2666
|
}>;
|
|
2667
|
-
|
|
2668
|
-
|
|
2667
|
+
/** DSL actions a widget could run, from the tools the app's origin offers. */
|
|
2668
|
+
suggestActions(appId: string, widgetType?: string): Promise<{
|
|
2669
|
+
suggestions: Array<{
|
|
2670
|
+
label: string;
|
|
2671
|
+
action: Record<string, unknown>;
|
|
2672
|
+
}>;
|
|
2669
2673
|
}>;
|
|
2670
2674
|
startSession(appId: string, pageId: string): Promise<{
|
|
2671
2675
|
sessionToken: string;
|
|
@@ -3067,6 +3071,8 @@ declare const ErrorCodes: {
|
|
|
3067
3071
|
readonly NOT_FOUND: "NOT_FOUND";
|
|
3068
3072
|
readonly ALREADY_EXISTS: "ALREADY_EXISTS";
|
|
3069
3073
|
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
3074
|
+
/** No refresh credential on this client; the session is intact but cannot be refreshed here. */
|
|
3075
|
+
readonly AUTH_REFRESH_UNAVAILABLE: "AUTH_REFRESH_UNAVAILABLE";
|
|
3070
3076
|
readonly TENANT_NOT_FOUND: "TENANT_NOT_FOUND";
|
|
3071
3077
|
readonly TENANT_SUSPENDED: "TENANT_SUSPENDED";
|
|
3072
3078
|
readonly NOT_A_MEMBER: "NOT_A_MEMBER";
|
package/dist/index.d.ts
CHANGED
|
@@ -224,6 +224,19 @@ declare class MAKEMINDSettlement {
|
|
|
224
224
|
description?: string;
|
|
225
225
|
metadata?: Record<string, unknown>;
|
|
226
226
|
}>;
|
|
227
|
+
/**
|
|
228
|
+
* What went out with the sale, recorded beside the money — at a price of
|
|
229
|
+
* nought too. One line per sale, at the gross.
|
|
230
|
+
*/
|
|
231
|
+
delivered?: {
|
|
232
|
+
name: string;
|
|
233
|
+
/** Defaults to 1. */
|
|
234
|
+
quantity?: number;
|
|
235
|
+
unit?: string;
|
|
236
|
+
productId?: string;
|
|
237
|
+
itemRef?: string;
|
|
238
|
+
subscriptionId?: string;
|
|
239
|
+
};
|
|
227
240
|
}): Promise<{
|
|
228
241
|
transactionId: string;
|
|
229
242
|
shareTransactionIds: (string | null)[];
|
|
@@ -243,6 +256,13 @@ declare class MAKEMINDSettlement {
|
|
|
243
256
|
reverseSaleWithShares(options: {
|
|
244
257
|
transactionId: string;
|
|
245
258
|
amount?: number;
|
|
259
|
+
/**
|
|
260
|
+
* Units that came back, when you know. Without it the ledger never guesses
|
|
261
|
+
* units from money: a refund short of the whole is a price adjustment (the
|
|
262
|
+
* thing stays delivered), and the refund that completes it returns every
|
|
263
|
+
* unit still out.
|
|
264
|
+
*/
|
|
265
|
+
returnedQuantity?: number;
|
|
246
266
|
reason?: string;
|
|
247
267
|
idempotencyKey: string;
|
|
248
268
|
}): Promise<{
|
|
@@ -583,12 +603,7 @@ declare class MAKEMINDAuth {
|
|
|
583
603
|
loadMemberships(): Promise<TenantMembership[]>;
|
|
584
604
|
signInWithEmail(email: string, password: string): Promise<AuthUser>;
|
|
585
605
|
signInWithEmailAndPassword(email: string, password: string): Promise<AuthUser>;
|
|
586
|
-
signUp(email: string, password: string, options?: {
|
|
587
|
-
displayName?: string;
|
|
588
|
-
phoneNumber?: string;
|
|
589
|
-
}): Promise<AuthUser>;
|
|
590
606
|
signOut(): Promise<void>;
|
|
591
|
-
sendPasswordResetEmail(email: string): Promise<void>;
|
|
592
607
|
onAuthStateChanged(callback: AuthStateCallback): () => void;
|
|
593
608
|
private notifyAuthStateCallbacks;
|
|
594
609
|
refreshAuthToken(): Promise<void>;
|
|
@@ -617,14 +632,6 @@ declare class MAKEMINDAuth {
|
|
|
617
632
|
* Backend dispatch 'resendVerificationEmail' — uid resolved from auth context, no payload needed.
|
|
618
633
|
*/
|
|
619
634
|
sendEmailVerification(): Promise<void>;
|
|
620
|
-
/**
|
|
621
|
-
* Verify email with code
|
|
622
|
-
*/
|
|
623
|
-
verifyEmail(code: string): Promise<void>;
|
|
624
|
-
/**
|
|
625
|
-
* Confirm password reset with code
|
|
626
|
-
*/
|
|
627
|
-
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
628
635
|
/**
|
|
629
636
|
* OAuth redirect-based sign-in is NOT supported by this backend.
|
|
630
637
|
* The backend has no /oauth/initiate or /oauth/callback routes.
|
|
@@ -707,6 +714,17 @@ declare class MAKEMINDData {
|
|
|
707
714
|
data: Record<string, unknown>;
|
|
708
715
|
documentId?: string;
|
|
709
716
|
}>): Promise<Array<DocumentResult<T>>>;
|
|
717
|
+
/**
|
|
718
|
+
* Send a batch and refuse to report a partial one as whole.
|
|
719
|
+
*
|
|
720
|
+
* The backend answers per item: an item it would not perform comes back
|
|
721
|
+
* `status: 'denied'` with the reason (`DATA_ALREADY_EXISTS` for a create over
|
|
722
|
+
* an id that exists, `DATA_WRITE_ONCE_VIOLATION`, `AUTH_ACCESS_DENIED`) while
|
|
723
|
+
* the others commit. Reading only the ids told the caller every item was
|
|
724
|
+
* written. A denied item now raises `BATCH_PARTIALLY_DENIED`, and the error
|
|
725
|
+
* carries every item's result so the caller can see which ones did commit.
|
|
726
|
+
*/
|
|
727
|
+
private batchWrite;
|
|
710
728
|
batchUpdate(collection: string, updates: Array<{
|
|
711
729
|
documentId: string;
|
|
712
730
|
data: Record<string, unknown>;
|
|
@@ -2099,7 +2117,6 @@ declare class MAKEMINDSearch {
|
|
|
2099
2117
|
*/
|
|
2100
2118
|
|
|
2101
2119
|
type AppStatus = 'draft' | 'published' | 'archived';
|
|
2102
|
-
type LayoutType = 'blank' | 'sidebar' | 'topnav' | 'dashboard' | 'centered';
|
|
2103
2120
|
type SSLStatus = 'pending' | 'provisioning' | 'active' | 'expired' | 'failed';
|
|
2104
2121
|
type CustomDomainStatus = 'pending' | 'verifying' | 'active' | 'failed';
|
|
2105
2122
|
/** Where a hostname's traffic is routed once it reaches the domain-serving edge. */
|
|
@@ -2208,18 +2225,17 @@ interface App {
|
|
|
2208
2225
|
publishedVersion: number | null;
|
|
2209
2226
|
domain: DomainConfig;
|
|
2210
2227
|
settings: AppSettings;
|
|
2228
|
+
definition?: AppDefinition;
|
|
2229
|
+
/** A version served to a share of visitors beside the published one. */
|
|
2230
|
+
rollout?: {
|
|
2231
|
+
version: number;
|
|
2232
|
+
percent: number;
|
|
2233
|
+
};
|
|
2211
2234
|
createdAt: string;
|
|
2212
2235
|
createdBy: string;
|
|
2213
2236
|
updatedAt: string;
|
|
2214
2237
|
updatedBy: string;
|
|
2215
2238
|
}
|
|
2216
|
-
interface LayoutConfig {
|
|
2217
|
-
sidebarWidth?: number;
|
|
2218
|
-
sidebarCollapsible?: boolean;
|
|
2219
|
-
headerHeight?: number;
|
|
2220
|
-
footerHeight?: number;
|
|
2221
|
-
contentMaxWidth?: number;
|
|
2222
|
-
}
|
|
2223
2239
|
interface PageSEO {
|
|
2224
2240
|
title: string;
|
|
2225
2241
|
description: string;
|
|
@@ -2227,61 +2243,42 @@ interface PageSEO {
|
|
|
2227
2243
|
noIndex?: boolean;
|
|
2228
2244
|
}
|
|
2229
2245
|
/**
|
|
2230
|
-
* A
|
|
2231
|
-
*
|
|
2232
|
-
* This described nine of the tree's fields while the editor writes fifteen:
|
|
2233
|
-
* `dataBinding`, `validation`, `a11y` and `conditions.className` were absent
|
|
2234
|
-
* entirely. TypeScript does not delete what it cannot see, so a round trip
|
|
2235
|
-
* through `getPage`/`updatePage` kept them — but a consumer BUILDING a node
|
|
2236
|
-
* from this type had no way to express a binding or a validation rule, and
|
|
2237
|
-
* one rebuilding a node field-by-field dropped them silently.
|
|
2246
|
+
* A widget of a page document — MCP UI DSL 1.4 (app builder DDD 08 §2).
|
|
2238
2247
|
*
|
|
2239
|
-
* `type`
|
|
2240
|
-
*
|
|
2241
|
-
*
|
|
2242
|
-
*
|
|
2243
|
-
* Kept in step with `@makemind/app-builder-ui`'s `ComponentNode` by
|
|
2244
|
-
* `src/__tests__/component-node-parity.test.ts`.
|
|
2248
|
+
* `type` is a widget type the spec defines; every node the builder holds
|
|
2249
|
+
* carries `x-id`. The other properties are the widget's own, as the spec
|
|
2250
|
+
* declares them, so they are not enumerated here.
|
|
2245
2251
|
*/
|
|
2246
|
-
interface
|
|
2247
|
-
id: string;
|
|
2252
|
+
interface DslNode {
|
|
2248
2253
|
type: string;
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2254
|
+
'x-id'?: string;
|
|
2255
|
+
[prop: string]: unknown;
|
|
2256
|
+
}
|
|
2257
|
+
/** A page document (page.schema). */
|
|
2258
|
+
interface DslPage {
|
|
2259
|
+
type: 'page';
|
|
2260
|
+
title?: string;
|
|
2261
|
+
content: DslNode;
|
|
2262
|
+
state?: {
|
|
2263
|
+
initial?: Record<string, unknown>;
|
|
2257
2264
|
};
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2265
|
+
lifecycle?: Record<string, unknown>;
|
|
2266
|
+
[prop: string]: unknown;
|
|
2267
|
+
}
|
|
2268
|
+
/** Theme, navigation, languages and app state — the DSL around the pages. */
|
|
2269
|
+
interface AppDefinition {
|
|
2270
|
+
title?: string;
|
|
2271
|
+
theme?: Record<string, unknown>;
|
|
2272
|
+
navigation?: {
|
|
2273
|
+
type: 'drawer' | 'bottomBar' | 'rail' | 'tabs';
|
|
2274
|
+
items?: unknown[];
|
|
2275
|
+
[key: string]: unknown;
|
|
2263
2276
|
};
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
message: string;
|
|
2268
|
-
}>;
|
|
2269
|
-
a11y?: {
|
|
2270
|
-
label?: string;
|
|
2271
|
-
description?: string;
|
|
2272
|
-
role?: string;
|
|
2277
|
+
i18n?: Record<string, unknown>;
|
|
2278
|
+
state?: {
|
|
2279
|
+
initial?: Record<string, unknown>;
|
|
2273
2280
|
};
|
|
2274
|
-
|
|
2275
|
-
interface DataSource {
|
|
2276
|
-
id: string;
|
|
2277
|
-
name: string;
|
|
2278
|
-
type: 'query' | 'document' | 'api' | 'flow' | 'variable' | 'realtime';
|
|
2279
|
-
config: Record<string, unknown>;
|
|
2280
|
-
}
|
|
2281
|
-
interface PageVariable {
|
|
2282
|
-
name: string;
|
|
2283
|
-
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
2284
|
-
defaultValue: unknown;
|
|
2281
|
+
[key: string]: unknown;
|
|
2285
2282
|
}
|
|
2286
2283
|
interface Page {
|
|
2287
2284
|
id: string;
|
|
@@ -2293,13 +2290,8 @@ interface Page {
|
|
|
2293
2290
|
isHomePage: boolean;
|
|
2294
2291
|
isPublic: boolean;
|
|
2295
2292
|
accessRoles: string[];
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
config: LayoutConfig;
|
|
2299
|
-
};
|
|
2300
|
-
components: ComponentNode[];
|
|
2301
|
-
dataSources: DataSource[];
|
|
2302
|
-
variables: PageVariable[];
|
|
2293
|
+
/** The page document the runner draws. */
|
|
2294
|
+
dsl: DslPage;
|
|
2303
2295
|
seo: PageSEO;
|
|
2304
2296
|
order: number;
|
|
2305
2297
|
createdAt: string;
|
|
@@ -2373,6 +2365,13 @@ interface UpdateAppParams {
|
|
|
2373
2365
|
icon?: string;
|
|
2374
2366
|
status?: AppStatus;
|
|
2375
2367
|
settings?: AppSettings;
|
|
2368
|
+
/** Replaces the app definition as a whole. */
|
|
2369
|
+
definition?: AppDefinition;
|
|
2370
|
+
/** Adjust a rollout; `percent: 100` promotes it, `null` ends it. */
|
|
2371
|
+
rollout?: {
|
|
2372
|
+
version: number;
|
|
2373
|
+
percent: number;
|
|
2374
|
+
} | null;
|
|
2376
2375
|
}
|
|
2377
2376
|
interface ListAppsParams {
|
|
2378
2377
|
status?: AppStatus;
|
|
@@ -2386,10 +2385,9 @@ interface CreatePageParams {
|
|
|
2386
2385
|
description?: string;
|
|
2387
2386
|
isHomePage?: boolean;
|
|
2388
2387
|
isPublic?: boolean;
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
};
|
|
2388
|
+
/** The page document; an empty column when absent. */
|
|
2389
|
+
dsl?: DslPage;
|
|
2390
|
+
seo?: Partial<PageSEO>;
|
|
2393
2391
|
}
|
|
2394
2392
|
interface UpdatePageParams {
|
|
2395
2393
|
name?: string;
|
|
@@ -2399,13 +2397,8 @@ interface UpdatePageParams {
|
|
|
2399
2397
|
isHomePage?: boolean;
|
|
2400
2398
|
isPublic?: boolean;
|
|
2401
2399
|
accessRoles?: string[];
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
config?: LayoutConfig;
|
|
2405
|
-
};
|
|
2406
|
-
components?: ComponentNode[];
|
|
2407
|
-
dataSources?: DataSource[];
|
|
2408
|
-
variables?: PageVariable[];
|
|
2400
|
+
/** Replaces the page document as a whole. */
|
|
2401
|
+
dsl?: DslPage;
|
|
2409
2402
|
seo?: PageSEO;
|
|
2410
2403
|
}
|
|
2411
2404
|
interface MAKEMINDAppsConfig extends MAKEMINDConfig {
|
|
@@ -2443,7 +2436,13 @@ declare class MAKEMINDApps {
|
|
|
2443
2436
|
duplicatePage(appId: string, pageId: string, newName?: string, newPath?: string): Promise<{
|
|
2444
2437
|
pageId: string;
|
|
2445
2438
|
}>;
|
|
2446
|
-
|
|
2439
|
+
/**
|
|
2440
|
+
* Cut a version and publish it — to everyone, or with `rolloutPercent`
|
|
2441
|
+
* (1–99) to that share of visitors beside the published version.
|
|
2442
|
+
*/
|
|
2443
|
+
publish(appId: string, changelog?: string, options?: {
|
|
2444
|
+
rolloutPercent?: number;
|
|
2445
|
+
}): Promise<{
|
|
2447
2446
|
success: boolean;
|
|
2448
2447
|
version: number;
|
|
2449
2448
|
publishedAt: string;
|
|
@@ -2661,11 +2660,16 @@ declare class MAKEMINDApps {
|
|
|
2661
2660
|
startDate?: string;
|
|
2662
2661
|
endDate?: string;
|
|
2663
2662
|
}): Promise<AppAnalytics>;
|
|
2663
|
+
/** A widget subtree in DSL from a description (app builder DDD 08 §9). */
|
|
2664
2664
|
generateUI(prompt: string, context?: Record<string, unknown>, appId?: string): Promise<{
|
|
2665
|
-
|
|
2665
|
+
content: DslNode;
|
|
2666
2666
|
}>;
|
|
2667
|
-
|
|
2668
|
-
|
|
2667
|
+
/** DSL actions a widget could run, from the tools the app's origin offers. */
|
|
2668
|
+
suggestActions(appId: string, widgetType?: string): Promise<{
|
|
2669
|
+
suggestions: Array<{
|
|
2670
|
+
label: string;
|
|
2671
|
+
action: Record<string, unknown>;
|
|
2672
|
+
}>;
|
|
2669
2673
|
}>;
|
|
2670
2674
|
startSession(appId: string, pageId: string): Promise<{
|
|
2671
2675
|
sessionToken: string;
|
|
@@ -3067,6 +3071,8 @@ declare const ErrorCodes: {
|
|
|
3067
3071
|
readonly NOT_FOUND: "NOT_FOUND";
|
|
3068
3072
|
readonly ALREADY_EXISTS: "ALREADY_EXISTS";
|
|
3069
3073
|
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
3074
|
+
/** No refresh credential on this client; the session is intact but cannot be refreshed here. */
|
|
3075
|
+
readonly AUTH_REFRESH_UNAVAILABLE: "AUTH_REFRESH_UNAVAILABLE";
|
|
3070
3076
|
readonly TENANT_NOT_FOUND: "TENANT_NOT_FOUND";
|
|
3071
3077
|
readonly TENANT_SUSPENDED: "TENANT_SUSPENDED";
|
|
3072
3078
|
readonly NOT_A_MEMBER: "NOT_A_MEMBER";
|
package/dist/index.js
CHANGED
|
@@ -101,6 +101,8 @@ var ErrorCodes = {
|
|
|
101
101
|
NOT_FOUND: "NOT_FOUND",
|
|
102
102
|
ALREADY_EXISTS: "ALREADY_EXISTS",
|
|
103
103
|
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
104
|
+
/** No refresh credential on this client; the session is intact but cannot be refreshed here. */
|
|
105
|
+
AUTH_REFRESH_UNAVAILABLE: "AUTH_REFRESH_UNAVAILABLE",
|
|
104
106
|
// Tenant errors
|
|
105
107
|
TENANT_NOT_FOUND: "TENANT_NOT_FOUND",
|
|
106
108
|
TENANT_SUSPENDED: "TENANT_SUSPENDED",
|
|
@@ -265,30 +267,17 @@ var MAKEMINDAuth = class {
|
|
|
265
267
|
async signInWithEmailAndPassword(email, password) {
|
|
266
268
|
return this.signInWithEmail(email, password);
|
|
267
269
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this._memberships = response.memberships || [];
|
|
280
|
-
this.setAuthToken(response.token);
|
|
281
|
-
this.persistSession(response.user, response.token, response.refreshToken, response.memberships);
|
|
282
|
-
this.onAuthStateChange(response.user);
|
|
283
|
-
this.notifyAuthStateCallbacks(response.user);
|
|
284
|
-
return response.user;
|
|
285
|
-
} catch (error) {
|
|
286
|
-
if (error instanceof MAKEMINDError) {
|
|
287
|
-
throw error;
|
|
288
|
-
}
|
|
289
|
-
throw new MAKEMINDError("AUTH_ERROR", "Failed to sign up");
|
|
290
|
-
}
|
|
291
|
-
}
|
|
270
|
+
/*
|
|
271
|
+
* `signUp` stood here. The platform registers nobody from this surface
|
|
272
|
+
* (2026-09-17): `/auth/signup` was removed and answers 404, so the method
|
|
273
|
+
* could only ever fail. A published client that offers an operation the
|
|
274
|
+
* server does not have is a lie the caller finds at runtime; it is removed
|
|
275
|
+
* rather than left to throw.
|
|
276
|
+
*
|
|
277
|
+
* Where accounts come from now: a platform account exists once its person
|
|
278
|
+
* signs in at the tenant portal; a tenant's own end users register in that
|
|
279
|
+
* tenant's pool (`endusers-selfRegisterEndUser`).
|
|
280
|
+
*/
|
|
292
281
|
async signOut() {
|
|
293
282
|
try {
|
|
294
283
|
await this.apiClient.post("/auth/signout", {});
|
|
@@ -303,12 +292,12 @@ var MAKEMINDAuth = class {
|
|
|
303
292
|
this.notifyAuthStateCallbacks(null);
|
|
304
293
|
}
|
|
305
294
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
295
|
+
/*
|
|
296
|
+
* `sendPasswordResetEmail` stood here, with `confirmPasswordReset` and
|
|
297
|
+
* `verifyEmail` below. All three routes were removed with the sign-up they
|
|
298
|
+
* belonged to: the reset ended at an email this platform could not send,
|
|
299
|
+
* while the API answered that a link had been sent.
|
|
300
|
+
*/
|
|
312
301
|
onAuthStateChanged(callback) {
|
|
313
302
|
this.authStateCallbacks.add(callback);
|
|
314
303
|
callback(this._currentUser);
|
|
@@ -344,7 +333,13 @@ var MAKEMINDAuth = class {
|
|
|
344
333
|
} else if (this._currentUser) {
|
|
345
334
|
this.persistSession(this._currentUser, response.token, response.refreshToken, response.memberships);
|
|
346
335
|
}
|
|
347
|
-
} catch {
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (error instanceof MAKEMINDError && error.code === ErrorCodes.VALIDATION_ERROR) {
|
|
338
|
+
throw new MAKEMINDError(
|
|
339
|
+
ErrorCodes.AUTH_REFRESH_UNAVAILABLE,
|
|
340
|
+
"This client holds no refresh credential; sign in again to pick up new claims"
|
|
341
|
+
);
|
|
342
|
+
}
|
|
348
343
|
await this.signOut();
|
|
349
344
|
throw new MAKEMINDError("UNAUTHENTICATED", "Session expired");
|
|
350
345
|
}
|
|
@@ -426,25 +421,6 @@ var MAKEMINDAuth = class {
|
|
|
426
421
|
}
|
|
427
422
|
await this.apiClient.dispatch("resendVerificationEmail");
|
|
428
423
|
}
|
|
429
|
-
/**
|
|
430
|
-
* Verify email with code
|
|
431
|
-
*/
|
|
432
|
-
async verifyEmail(code) {
|
|
433
|
-
await this.apiClient.post("/auth/verify-email", {
|
|
434
|
-
code,
|
|
435
|
-
applicationId: this.applicationId
|
|
436
|
-
}, { skipAuth: true });
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Confirm password reset with code
|
|
440
|
-
*/
|
|
441
|
-
async confirmPasswordReset(code, newPassword) {
|
|
442
|
-
await this.apiClient.post("/auth/confirm-reset", {
|
|
443
|
-
code,
|
|
444
|
-
newPassword,
|
|
445
|
-
applicationId: this.applicationId
|
|
446
|
-
}, { skipAuth: true });
|
|
447
|
-
}
|
|
448
424
|
/**
|
|
449
425
|
* OAuth redirect-based sign-in is NOT supported by this backend.
|
|
450
426
|
* The backend has no /oauth/initiate or /oauth/callback routes.
|
|
@@ -601,31 +577,49 @@ var MAKEMINDData = class {
|
|
|
601
577
|
metadata: { hasMore: res.hasMore ?? false, lastDocId: res.nextCursor }
|
|
602
578
|
};
|
|
603
579
|
}
|
|
580
|
+
/*
|
|
581
|
+
* Every method on this service addresses the document platform — the store
|
|
582
|
+
* `query`, the batch methods, `aggregate` and `search` already read. The
|
|
583
|
+
* single-document methods used to go through the REST data door, which
|
|
584
|
+
* keeps a separate tree with a fixed set of collection names: a document
|
|
585
|
+
* created there was invisible to `query` and refused by `batchDelete`, and
|
|
586
|
+
* a collection of the tenant's own naming was refused at the door. One
|
|
587
|
+
* object, one store.
|
|
588
|
+
*/
|
|
604
589
|
async get(options) {
|
|
605
590
|
try {
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
);
|
|
609
|
-
return
|
|
591
|
+
const doc = await this.apiClient.dispatch("dataGetDocument", {
|
|
592
|
+
id: options.documentId
|
|
593
|
+
});
|
|
594
|
+
return { id: doc.id ?? options.documentId, data: doc.data };
|
|
610
595
|
} catch (error) {
|
|
611
|
-
if (error instanceof MAKEMINDError && error.code === "NOT_FOUND") {
|
|
596
|
+
if (error instanceof MAKEMINDError && (error.code === "NOT_FOUND" || error.code === "DATA_NOT_FOUND")) {
|
|
612
597
|
return null;
|
|
613
598
|
}
|
|
614
599
|
throw error;
|
|
615
600
|
}
|
|
616
601
|
}
|
|
617
602
|
async create(options) {
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
603
|
+
const created = await this.apiClient.dispatch(
|
|
604
|
+
"dataCreateDocument",
|
|
605
|
+
{
|
|
606
|
+
collection: options.collection,
|
|
607
|
+
data: options.data,
|
|
608
|
+
id: options.documentId
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
return { id: created.id, data: options.data };
|
|
623
612
|
}
|
|
624
613
|
async update(options) {
|
|
625
|
-
await this.apiClient.
|
|
614
|
+
await this.apiClient.dispatch("dataUpdateDocument", {
|
|
615
|
+
id: options.documentId,
|
|
616
|
+
data: options.data
|
|
617
|
+
});
|
|
626
618
|
}
|
|
627
619
|
async delete(options) {
|
|
628
|
-
await this.apiClient.
|
|
620
|
+
await this.apiClient.dispatch("dataDeleteDocument", {
|
|
621
|
+
id: options.documentId
|
|
622
|
+
});
|
|
629
623
|
}
|
|
630
624
|
onSnapshot(options, callback) {
|
|
631
625
|
const subscriptionId = `doc:${options.collection}:${options.documentId}`;
|
|
@@ -743,11 +737,33 @@ var MAKEMINDData = class {
|
|
|
743
737
|
...documentId ? { id: documentId } : {},
|
|
744
738
|
data
|
|
745
739
|
}));
|
|
740
|
+
const results = await this.batchWrite(operations);
|
|
741
|
+
return results.map((r) => ({ id: r.id, data: {} }));
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Send a batch and refuse to report a partial one as whole.
|
|
745
|
+
*
|
|
746
|
+
* The backend answers per item: an item it would not perform comes back
|
|
747
|
+
* `status: 'denied'` with the reason (`DATA_ALREADY_EXISTS` for a create over
|
|
748
|
+
* an id that exists, `DATA_WRITE_ONCE_VIOLATION`, `AUTH_ACCESS_DENIED`) while
|
|
749
|
+
* the others commit. Reading only the ids told the caller every item was
|
|
750
|
+
* written. A denied item now raises `BATCH_PARTIALLY_DENIED`, and the error
|
|
751
|
+
* carries every item's result so the caller can see which ones did commit.
|
|
752
|
+
*/
|
|
753
|
+
async batchWrite(operations) {
|
|
746
754
|
const response = await this.apiClient.dispatch(
|
|
747
755
|
"dataBatchWrite",
|
|
748
756
|
{ operations }
|
|
749
757
|
);
|
|
750
|
-
|
|
758
|
+
const denied = response.results.filter((r) => r.status === "denied");
|
|
759
|
+
if (denied.length > 0) {
|
|
760
|
+
throw new MAKEMINDError(
|
|
761
|
+
"BATCH_PARTIALLY_DENIED",
|
|
762
|
+
`${denied.length} of ${response.results.length} batch operations were refused: ` + denied.map((r) => `${r.id} (${r.error ?? "denied"})`).join(", "),
|
|
763
|
+
{ results: response.results, denied }
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
return response.results;
|
|
751
767
|
}
|
|
752
768
|
async batchUpdate(collection, updates) {
|
|
753
769
|
const operations = updates.map(({ documentId, data }) => ({
|
|
@@ -755,14 +771,14 @@ var MAKEMINDData = class {
|
|
|
755
771
|
id: documentId,
|
|
756
772
|
data
|
|
757
773
|
}));
|
|
758
|
-
await this.
|
|
774
|
+
await this.batchWrite(operations);
|
|
759
775
|
}
|
|
760
776
|
async batchDelete(collection, documentIds) {
|
|
761
777
|
const operations = documentIds.map((id) => ({
|
|
762
778
|
type: "delete",
|
|
763
779
|
id
|
|
764
780
|
}));
|
|
765
|
-
await this.
|
|
781
|
+
await this.batchWrite(operations);
|
|
766
782
|
}
|
|
767
783
|
// Export/Import operations
|
|
768
784
|
//
|
|
@@ -3081,8 +3097,12 @@ var MAKEMINDApps = class {
|
|
|
3081
3097
|
// ============================================
|
|
3082
3098
|
// Publishing & Versioning
|
|
3083
3099
|
// ============================================
|
|
3084
|
-
|
|
3085
|
-
|
|
3100
|
+
/**
|
|
3101
|
+
* Cut a version and publish it — to everyone, or with `rolloutPercent`
|
|
3102
|
+
* (1–99) to that share of visitors beside the published version.
|
|
3103
|
+
*/
|
|
3104
|
+
async publish(appId, changelog, options = {}) {
|
|
3105
|
+
const response = await this.apiClient.post(`/apps/${appId}/publish`, { changelog, ...options });
|
|
3086
3106
|
return response;
|
|
3087
3107
|
}
|
|
3088
3108
|
async unpublish(appId) {
|
|
@@ -3281,16 +3301,20 @@ var MAKEMINDApps = class {
|
|
|
3281
3301
|
// ============================================
|
|
3282
3302
|
// AI Features
|
|
3283
3303
|
// ============================================
|
|
3304
|
+
/** A widget subtree in DSL from a description (app builder DDD 08 §9). */
|
|
3284
3305
|
async generateUI(prompt, context, appId) {
|
|
3285
3306
|
const response = await this.apiClient.post(
|
|
3286
3307
|
"/apps/ai/generate-ui",
|
|
3287
3308
|
{ prompt, context, appId }
|
|
3288
3309
|
);
|
|
3289
|
-
return {
|
|
3310
|
+
return { content: response.content };
|
|
3290
3311
|
}
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
return
|
|
3312
|
+
/** DSL actions a widget could run, from the tools the app's origin offers. */
|
|
3313
|
+
async suggestActions(appId, widgetType) {
|
|
3314
|
+
return this.apiClient.post(
|
|
3315
|
+
`/apps/${appId}/ai/suggest-actions`,
|
|
3316
|
+
{ widgetType }
|
|
3317
|
+
);
|
|
3294
3318
|
}
|
|
3295
3319
|
// ============================================
|
|
3296
3320
|
// Collaboration
|
package/dist/index.mjs
CHANGED
|
@@ -45,6 +45,8 @@ var ErrorCodes = {
|
|
|
45
45
|
NOT_FOUND: "NOT_FOUND",
|
|
46
46
|
ALREADY_EXISTS: "ALREADY_EXISTS",
|
|
47
47
|
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
48
|
+
/** No refresh credential on this client; the session is intact but cannot be refreshed here. */
|
|
49
|
+
AUTH_REFRESH_UNAVAILABLE: "AUTH_REFRESH_UNAVAILABLE",
|
|
48
50
|
// Tenant errors
|
|
49
51
|
TENANT_NOT_FOUND: "TENANT_NOT_FOUND",
|
|
50
52
|
TENANT_SUSPENDED: "TENANT_SUSPENDED",
|
|
@@ -209,30 +211,17 @@ var MAKEMINDAuth = class {
|
|
|
209
211
|
async signInWithEmailAndPassword(email, password) {
|
|
210
212
|
return this.signInWithEmail(email, password);
|
|
211
213
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
this._memberships = response.memberships || [];
|
|
224
|
-
this.setAuthToken(response.token);
|
|
225
|
-
this.persistSession(response.user, response.token, response.refreshToken, response.memberships);
|
|
226
|
-
this.onAuthStateChange(response.user);
|
|
227
|
-
this.notifyAuthStateCallbacks(response.user);
|
|
228
|
-
return response.user;
|
|
229
|
-
} catch (error) {
|
|
230
|
-
if (error instanceof MAKEMINDError) {
|
|
231
|
-
throw error;
|
|
232
|
-
}
|
|
233
|
-
throw new MAKEMINDError("AUTH_ERROR", "Failed to sign up");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
214
|
+
/*
|
|
215
|
+
* `signUp` stood here. The platform registers nobody from this surface
|
|
216
|
+
* (2026-09-17): `/auth/signup` was removed and answers 404, so the method
|
|
217
|
+
* could only ever fail. A published client that offers an operation the
|
|
218
|
+
* server does not have is a lie the caller finds at runtime; it is removed
|
|
219
|
+
* rather than left to throw.
|
|
220
|
+
*
|
|
221
|
+
* Where accounts come from now: a platform account exists once its person
|
|
222
|
+
* signs in at the tenant portal; a tenant's own end users register in that
|
|
223
|
+
* tenant's pool (`endusers-selfRegisterEndUser`).
|
|
224
|
+
*/
|
|
236
225
|
async signOut() {
|
|
237
226
|
try {
|
|
238
227
|
await this.apiClient.post("/auth/signout", {});
|
|
@@ -247,12 +236,12 @@ var MAKEMINDAuth = class {
|
|
|
247
236
|
this.notifyAuthStateCallbacks(null);
|
|
248
237
|
}
|
|
249
238
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
239
|
+
/*
|
|
240
|
+
* `sendPasswordResetEmail` stood here, with `confirmPasswordReset` and
|
|
241
|
+
* `verifyEmail` below. All three routes were removed with the sign-up they
|
|
242
|
+
* belonged to: the reset ended at an email this platform could not send,
|
|
243
|
+
* while the API answered that a link had been sent.
|
|
244
|
+
*/
|
|
256
245
|
onAuthStateChanged(callback) {
|
|
257
246
|
this.authStateCallbacks.add(callback);
|
|
258
247
|
callback(this._currentUser);
|
|
@@ -288,7 +277,13 @@ var MAKEMINDAuth = class {
|
|
|
288
277
|
} else if (this._currentUser) {
|
|
289
278
|
this.persistSession(this._currentUser, response.token, response.refreshToken, response.memberships);
|
|
290
279
|
}
|
|
291
|
-
} catch {
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (error instanceof MAKEMINDError && error.code === ErrorCodes.VALIDATION_ERROR) {
|
|
282
|
+
throw new MAKEMINDError(
|
|
283
|
+
ErrorCodes.AUTH_REFRESH_UNAVAILABLE,
|
|
284
|
+
"This client holds no refresh credential; sign in again to pick up new claims"
|
|
285
|
+
);
|
|
286
|
+
}
|
|
292
287
|
await this.signOut();
|
|
293
288
|
throw new MAKEMINDError("UNAUTHENTICATED", "Session expired");
|
|
294
289
|
}
|
|
@@ -370,25 +365,6 @@ var MAKEMINDAuth = class {
|
|
|
370
365
|
}
|
|
371
366
|
await this.apiClient.dispatch("resendVerificationEmail");
|
|
372
367
|
}
|
|
373
|
-
/**
|
|
374
|
-
* Verify email with code
|
|
375
|
-
*/
|
|
376
|
-
async verifyEmail(code) {
|
|
377
|
-
await this.apiClient.post("/auth/verify-email", {
|
|
378
|
-
code,
|
|
379
|
-
applicationId: this.applicationId
|
|
380
|
-
}, { skipAuth: true });
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Confirm password reset with code
|
|
384
|
-
*/
|
|
385
|
-
async confirmPasswordReset(code, newPassword) {
|
|
386
|
-
await this.apiClient.post("/auth/confirm-reset", {
|
|
387
|
-
code,
|
|
388
|
-
newPassword,
|
|
389
|
-
applicationId: this.applicationId
|
|
390
|
-
}, { skipAuth: true });
|
|
391
|
-
}
|
|
392
368
|
/**
|
|
393
369
|
* OAuth redirect-based sign-in is NOT supported by this backend.
|
|
394
370
|
* The backend has no /oauth/initiate or /oauth/callback routes.
|
|
@@ -545,31 +521,49 @@ var MAKEMINDData = class {
|
|
|
545
521
|
metadata: { hasMore: res.hasMore ?? false, lastDocId: res.nextCursor }
|
|
546
522
|
};
|
|
547
523
|
}
|
|
524
|
+
/*
|
|
525
|
+
* Every method on this service addresses the document platform — the store
|
|
526
|
+
* `query`, the batch methods, `aggregate` and `search` already read. The
|
|
527
|
+
* single-document methods used to go through the REST data door, which
|
|
528
|
+
* keeps a separate tree with a fixed set of collection names: a document
|
|
529
|
+
* created there was invisible to `query` and refused by `batchDelete`, and
|
|
530
|
+
* a collection of the tenant's own naming was refused at the door. One
|
|
531
|
+
* object, one store.
|
|
532
|
+
*/
|
|
548
533
|
async get(options) {
|
|
549
534
|
try {
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
);
|
|
553
|
-
return
|
|
535
|
+
const doc = await this.apiClient.dispatch("dataGetDocument", {
|
|
536
|
+
id: options.documentId
|
|
537
|
+
});
|
|
538
|
+
return { id: doc.id ?? options.documentId, data: doc.data };
|
|
554
539
|
} catch (error) {
|
|
555
|
-
if (error instanceof MAKEMINDError && error.code === "NOT_FOUND") {
|
|
540
|
+
if (error instanceof MAKEMINDError && (error.code === "NOT_FOUND" || error.code === "DATA_NOT_FOUND")) {
|
|
556
541
|
return null;
|
|
557
542
|
}
|
|
558
543
|
throw error;
|
|
559
544
|
}
|
|
560
545
|
}
|
|
561
546
|
async create(options) {
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
547
|
+
const created = await this.apiClient.dispatch(
|
|
548
|
+
"dataCreateDocument",
|
|
549
|
+
{
|
|
550
|
+
collection: options.collection,
|
|
551
|
+
data: options.data,
|
|
552
|
+
id: options.documentId
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
return { id: created.id, data: options.data };
|
|
567
556
|
}
|
|
568
557
|
async update(options) {
|
|
569
|
-
await this.apiClient.
|
|
558
|
+
await this.apiClient.dispatch("dataUpdateDocument", {
|
|
559
|
+
id: options.documentId,
|
|
560
|
+
data: options.data
|
|
561
|
+
});
|
|
570
562
|
}
|
|
571
563
|
async delete(options) {
|
|
572
|
-
await this.apiClient.
|
|
564
|
+
await this.apiClient.dispatch("dataDeleteDocument", {
|
|
565
|
+
id: options.documentId
|
|
566
|
+
});
|
|
573
567
|
}
|
|
574
568
|
onSnapshot(options, callback) {
|
|
575
569
|
const subscriptionId = `doc:${options.collection}:${options.documentId}`;
|
|
@@ -687,11 +681,33 @@ var MAKEMINDData = class {
|
|
|
687
681
|
...documentId ? { id: documentId } : {},
|
|
688
682
|
data
|
|
689
683
|
}));
|
|
684
|
+
const results = await this.batchWrite(operations);
|
|
685
|
+
return results.map((r) => ({ id: r.id, data: {} }));
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Send a batch and refuse to report a partial one as whole.
|
|
689
|
+
*
|
|
690
|
+
* The backend answers per item: an item it would not perform comes back
|
|
691
|
+
* `status: 'denied'` with the reason (`DATA_ALREADY_EXISTS` for a create over
|
|
692
|
+
* an id that exists, `DATA_WRITE_ONCE_VIOLATION`, `AUTH_ACCESS_DENIED`) while
|
|
693
|
+
* the others commit. Reading only the ids told the caller every item was
|
|
694
|
+
* written. A denied item now raises `BATCH_PARTIALLY_DENIED`, and the error
|
|
695
|
+
* carries every item's result so the caller can see which ones did commit.
|
|
696
|
+
*/
|
|
697
|
+
async batchWrite(operations) {
|
|
690
698
|
const response = await this.apiClient.dispatch(
|
|
691
699
|
"dataBatchWrite",
|
|
692
700
|
{ operations }
|
|
693
701
|
);
|
|
694
|
-
|
|
702
|
+
const denied = response.results.filter((r) => r.status === "denied");
|
|
703
|
+
if (denied.length > 0) {
|
|
704
|
+
throw new MAKEMINDError(
|
|
705
|
+
"BATCH_PARTIALLY_DENIED",
|
|
706
|
+
`${denied.length} of ${response.results.length} batch operations were refused: ` + denied.map((r) => `${r.id} (${r.error ?? "denied"})`).join(", "),
|
|
707
|
+
{ results: response.results, denied }
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
return response.results;
|
|
695
711
|
}
|
|
696
712
|
async batchUpdate(collection, updates) {
|
|
697
713
|
const operations = updates.map(({ documentId, data }) => ({
|
|
@@ -699,14 +715,14 @@ var MAKEMINDData = class {
|
|
|
699
715
|
id: documentId,
|
|
700
716
|
data
|
|
701
717
|
}));
|
|
702
|
-
await this.
|
|
718
|
+
await this.batchWrite(operations);
|
|
703
719
|
}
|
|
704
720
|
async batchDelete(collection, documentIds) {
|
|
705
721
|
const operations = documentIds.map((id) => ({
|
|
706
722
|
type: "delete",
|
|
707
723
|
id
|
|
708
724
|
}));
|
|
709
|
-
await this.
|
|
725
|
+
await this.batchWrite(operations);
|
|
710
726
|
}
|
|
711
727
|
// Export/Import operations
|
|
712
728
|
//
|
|
@@ -3025,8 +3041,12 @@ var MAKEMINDApps = class {
|
|
|
3025
3041
|
// ============================================
|
|
3026
3042
|
// Publishing & Versioning
|
|
3027
3043
|
// ============================================
|
|
3028
|
-
|
|
3029
|
-
|
|
3044
|
+
/**
|
|
3045
|
+
* Cut a version and publish it — to everyone, or with `rolloutPercent`
|
|
3046
|
+
* (1–99) to that share of visitors beside the published version.
|
|
3047
|
+
*/
|
|
3048
|
+
async publish(appId, changelog, options = {}) {
|
|
3049
|
+
const response = await this.apiClient.post(`/apps/${appId}/publish`, { changelog, ...options });
|
|
3030
3050
|
return response;
|
|
3031
3051
|
}
|
|
3032
3052
|
async unpublish(appId) {
|
|
@@ -3225,16 +3245,20 @@ var MAKEMINDApps = class {
|
|
|
3225
3245
|
// ============================================
|
|
3226
3246
|
// AI Features
|
|
3227
3247
|
// ============================================
|
|
3248
|
+
/** A widget subtree in DSL from a description (app builder DDD 08 §9). */
|
|
3228
3249
|
async generateUI(prompt, context, appId) {
|
|
3229
3250
|
const response = await this.apiClient.post(
|
|
3230
3251
|
"/apps/ai/generate-ui",
|
|
3231
3252
|
{ prompt, context, appId }
|
|
3232
3253
|
);
|
|
3233
|
-
return {
|
|
3254
|
+
return { content: response.content };
|
|
3234
3255
|
}
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
return
|
|
3256
|
+
/** DSL actions a widget could run, from the tools the app's origin offers. */
|
|
3257
|
+
async suggestActions(appId, widgetType) {
|
|
3258
|
+
return this.apiClient.post(
|
|
3259
|
+
`/apps/${appId}/ai/suggest-actions`,
|
|
3260
|
+
{ widgetType }
|
|
3261
|
+
);
|
|
3238
3262
|
}
|
|
3239
3263
|
// ============================================
|
|
3240
3264
|
// Collaboration
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makemind/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MAKEMIND Platform SDK for Web Applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -34,15 +34,17 @@
|
|
|
34
34
|
"firebase": "^10.14.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
+
"@makemind/app-builder-ui": "file:../app-builder-ui",
|
|
37
38
|
"@types/jsdom": "^30.0.0",
|
|
38
39
|
"@types/node": "^20.10.0",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
41
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
39
42
|
"eslint": "^8.55.0",
|
|
40
43
|
"firebase": "^10.7.0",
|
|
41
44
|
"jsdom": "^30.0.1",
|
|
42
45
|
"tsup": "^8.0.1",
|
|
43
46
|
"typescript": "^5.3.0",
|
|
44
|
-
"vitest": "^1.0.0"
|
|
45
|
-
"@makemind/app-builder-ui": "file:../app-builder-ui"
|
|
47
|
+
"vitest": "^1.0.0"
|
|
46
48
|
},
|
|
47
49
|
"keywords": [
|
|
48
50
|
"makemind",
|