@makemind/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@makemind/sdk` will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 0.1.0
9
+
10
+ First release. The public surface is not frozen: while this package is 0.x, a minor bump may change it.
11
+
12
+ ### Added
13
+
14
+ ### Corrected before first release
15
+
16
+ - `FlowListItem` described six fields while `GET /logic/flows` returns
17
+ eighteen, so consumers cast the row to read `publicExecution` or `mode`. It
18
+ now describes the payload.
19
+ - `FlowListItem.status` declared `'active'`, a value the route never sends: a
20
+ published flow is stored and returned as `'published'`.
21
+ - `ComponentNode` omitted `dataBinding`, `validation`, `a11y` and
22
+ `conditions.className`. A consumer building a node from this type could not
23
+ express a data binding at all. Kept in step with the App Builder's own
24
+ declaration by `component-node-parity.test.ts`.
25
+ - `listFlows()` took no arguments and returned a bare array. It now accepts
26
+ `status`, `mode`, `limit` and `cursor` — the gateway has always supported
27
+ them — and returns `{ flows, hasMore, nextCursor }`, so a truncated page is
28
+ distinguishable from a complete one.
29
+
30
+ - `initializeMAKEMIND()` / `getMAKEMIND()` entry points for configuring the SDK
31
+ with an API key, application ID, environment, and optional gateway `baseUrl`
32
+ override.
33
+ - Optional Firebase App Check integration (`getAppCheckToken`) attached as the
34
+ `X-Firebase-AppCheck` header on every gateway call.
35
+ - Eleven typed platform services, each a thin client over the MAKEMIND API
36
+ gateway:
37
+ - `auth` — email/password sign-in and sign-up, auth state subscription,
38
+ tenant memberships, tenant switching, sign-out.
39
+ - `data` — tenant-isolated document query/get/create/update and real-time
40
+ collection subscriptions.
41
+ - `logic` — workflow transitions (ECO, NCR, marketplace bid, gate approval)
42
+ and generic flow execution.
43
+ - `tenant` — tenant profile, settings, members, invitations, and
44
+ workspaces.
45
+ - `messaging` — notification send, real-time notification subscription,
46
+ and mark-as-read.
47
+ - `settlement` — usage-event recording, usage stats, billing summary,
48
+ quota status, and invoices.
49
+ - `storage` — file upload/download, metadata, listing, copy/move/delete,
50
+ PLM document helpers, and bucket management.
51
+ - `ai` — chat completion (including streaming and tool calling),
52
+ embeddings, vector search, vision analysis, and text
53
+ generate/summarize/translate helpers.
54
+ - `search` — full-text query, advanced faceted search, autocomplete
55
+ suggestions, analytics, and bulk reindexing.
56
+ - `apps` — App Builder app/page CRUD, publishing and versioning, domain
57
+ management, AI-assisted UI generation, and live-edit collaboration
58
+ sessions.
59
+ - `marketplace` — listing discovery/search, install/uninstall, app
60
+ runtime proxying, reviews, and the publisher/admin publishing
61
+ lifecycle.
62
+ - `call(operation, input)` generic RPC dispatch for platform Callable
63
+ operations that do not yet have a dedicated typed service method.
64
+ - `MAKEMINDError` / `ErrorCodes` for structured error handling across all
65
+ services.
66
+ - `firebase` as an **optional** peer dependency. It is loaded on demand by the
67
+ push-notification methods and by App Check token retrieval; the SDK imports
68
+ and every gateway call work with `firebase` absent.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MAKEMIND
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,550 @@
1
+ # @makemind/sdk
2
+
3
+ MAKEMIND Platform SDK for Web Applications (React, Vue, Angular, etc.)
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @makemind/sdk
9
+ ```
10
+
11
+ The SDK talks to the MAKEMIND API gateway over HTTPS. Installing it pulls in
12
+ nothing else: you only need an API key and your application ID.
13
+
14
+ `firebase` is an **optional** peer. Two features load it on demand and only
15
+ when you use them — web push (`messaging.registerDevice`,
16
+ `messaging.onPushMessage`) and App Check tokens. Everything else, including
17
+ every gateway call, works with `firebase` absent.
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { initializeMAKEMIND, getMAKEMIND } from '@makemind/sdk';
23
+
24
+ // Initialize MAKEMIND SDK
25
+ initializeMAKEMIND({
26
+ apiKey: 'your-api-key',
27
+ applicationId: 'plm', // Your application ID
28
+ environment: 'development',
29
+ // Optional: override the gateway base URL (defaults to https://api.makemind.app).
30
+ // Point this at a locally running gateway during development.
31
+ baseUrl: 'http://127.0.0.1:5001/<project-id>/us-central1/apiGateway',
32
+ });
33
+
34
+ // Use the SDK
35
+ const makemind = getMAKEMIND();
36
+ ```
37
+
38
+ `initializeMAKEMIND` accepts `{ apiKey, applicationId, environment?, baseUrl?, apiVersion?, getAppCheckToken? }`.
39
+ All service calls are issued against `${baseUrl}/api/${apiVersion}` (`apiVersion`
40
+ defaults to `v1`).
41
+
42
+ ### App Check (first-party apps only)
43
+
44
+ Pass `getAppCheckToken` — an async provider returning a Firebase App Check
45
+ token — to have the SDK attach it as `X-Firebase-AppCheck` on every gateway
46
+ call. This lets browser/mobile apps pass App Check enforcement on the pre-login
47
+ endpoints (signin/signup/password-reset/oauth). Backward-compatible: omit it for
48
+ M2M / API-key integrations (exempt from App Check server-side), and a provider
49
+ returning `null` simply omits the header.
50
+
51
+ ```typescript
52
+ import { getToken } from 'firebase/app-check';
53
+ initializeMAKEMIND({
54
+ apiKey, applicationId,
55
+ getAppCheckToken: async () => (await getToken(appCheck, false)).token,
56
+ });
57
+ ```
58
+
59
+ ## Authentication
60
+
61
+ ```typescript
62
+ const makemind = getMAKEMIND();
63
+
64
+ // Sign in with email + password
65
+ const user = await makemind.auth.signInWithEmail(email, password);
66
+
67
+ // Sign up a new user
68
+ const newUser = await makemind.auth.signUp(email, password, {
69
+ displayName: 'Jane Doe',
70
+ });
71
+
72
+ // Listen to auth state changes
73
+ const unsubscribe = makemind.auth.onAuthStateChanged((user) => {
74
+ if (user) {
75
+ console.log('User signed in:', user.uid);
76
+ console.log('Tenant:', user.tenantId);
77
+ console.log('Roles:', user.roles);
78
+ }
79
+ });
80
+
81
+ // Sign out
82
+ await makemind.auth.signOut();
83
+
84
+ // Get user memberships (tenants user belongs to)
85
+ const memberships = await makemind.auth.getUserMemberships();
86
+
87
+ // Switch tenant
88
+ await makemind.auth.switchTenant('new-tenant-id');
89
+ ```
90
+
91
+ ## Data Operations
92
+
93
+ ```typescript
94
+ const makemind = getMAKEMIND();
95
+
96
+ // Query with tenant isolation
97
+ const { data: products } = await makemind.data.query({
98
+ collection: 'plm_products',
99
+ where: [
100
+ { field: 'tenant_id', op: '==', value: makemind.tenantId },
101
+ { field: 'status', op: '==', value: 'active' },
102
+ ],
103
+ orderBy: [{ field: 'created_at', direction: 'desc' }],
104
+ limit: 20,
105
+ });
106
+
107
+ // Get single document
108
+ const product = await makemind.data.get({
109
+ collection: 'plm_products',
110
+ documentId: 'product-id',
111
+ });
112
+
113
+ // Create document (tenant_id auto-added)
114
+ const result = await makemind.data.create({
115
+ collection: 'plm_products',
116
+ data: {
117
+ product_code: 'PRD-001',
118
+ product_name: 'Sample Product',
119
+ status: 'development',
120
+ },
121
+ });
122
+
123
+ // Update document
124
+ await makemind.data.update({
125
+ collection: 'plm_products',
126
+ documentId: 'product-id',
127
+ data: { status: 'active' },
128
+ });
129
+
130
+ // Real-time subscription
131
+ const unsubscribe = makemind.data.onCollectionSnapshot(
132
+ {
133
+ collection: 'plm_products',
134
+ where: [{ field: 'tenant_id', op: '==', value: makemind.tenantId }],
135
+ },
136
+ (products) => {
137
+ console.log('Products updated:', products);
138
+ }
139
+ );
140
+
141
+ // Cleanup
142
+ unsubscribe();
143
+ ```
144
+
145
+ ## Logic / Workflows
146
+
147
+ ```typescript
148
+ const makemind = getMAKEMIND();
149
+
150
+ // ECO workflow transition
151
+ await makemind.logic.ecoTransition('eco-id', 'submit');
152
+ await makemind.logic.ecoTransition('eco-id', 'approve', {
153
+ comments: 'Approved for implementation',
154
+ });
155
+
156
+ // NCR workflow
157
+ await makemind.logic.ncrTransition('issue-id', 'assignContainment', {
158
+ assignedTo: 'user-id',
159
+ containmentAction: 'Quarantine affected parts',
160
+ });
161
+
162
+ // Marketplace bid
163
+ await makemind.logic.bidTransition('request-id', 'bid-id', 'submit');
164
+
165
+ // Gate approval
166
+ await makemind.logic.gateApproval('product-id', 'production', 'approve');
167
+
168
+ // Generic flow execution
169
+ const result = await makemind.logic.executeFlow('flow-id', {
170
+ input: 'data',
171
+ });
172
+ ```
173
+
174
+ ## Tenant Management
175
+
176
+ ```typescript
177
+ const makemind = getMAKEMIND();
178
+
179
+ // Get current tenant
180
+ const tenant = await makemind.tenant.getCurrent();
181
+
182
+ // Update tenant settings
183
+ await makemind.tenant.updateTenant({
184
+ settings: { industryType: 'electronics' },
185
+ });
186
+
187
+ // List members
188
+ const members = await makemind.tenant.listMembers();
189
+
190
+ // Invite member
191
+ await makemind.tenant.inviteMember({
192
+ email: 'new@example.com',
193
+ role: 'manager',
194
+ });
195
+
196
+ // Workspaces
197
+ const workspaces = await makemind.tenant.listWorkspaces();
198
+ await makemind.tenant.createWorkspace({
199
+ name: 'Development Team',
200
+ description: 'R&D workspace',
201
+ });
202
+ ```
203
+
204
+ ## Messaging / Notifications
205
+
206
+ ```typescript
207
+ const makemind = getMAKEMIND();
208
+
209
+ // Send notification
210
+ await makemind.messaging.send({
211
+ template: 'ecoReviewRequest',
212
+ recipients: ['user-1', 'user-2'],
213
+ data: {
214
+ ecoNumber: 'ECO-2024-001',
215
+ title: 'Design Change Request',
216
+ },
217
+ channels: ['push', 'email'],
218
+ });
219
+
220
+ // Listen to notifications
221
+ const unsubscribe = makemind.messaging.onNotifications(
222
+ (notifications) => {
223
+ console.log('Notifications:', notifications);
224
+ },
225
+ { unreadOnly: true }
226
+ );
227
+
228
+ // Mark as read
229
+ await makemind.messaging.markAsRead('notification-id');
230
+ ```
231
+
232
+ ## Settlement / Billing
233
+
234
+ ```typescript
235
+ const makemind = getMAKEMIND();
236
+
237
+ // Record usage event
238
+ await makemind.settlement.recordProductCreation('product-id');
239
+ await makemind.settlement.recordDocumentUpload('doc-id', 2.5); // 2.5 MB
240
+
241
+ // Get usage stats
242
+ const stats = await makemind.settlement.getUsageStats('currentMonth');
243
+
244
+ // Get billing summary
245
+ const billing = await makemind.settlement.getBillingSummary();
246
+
247
+ // Check quotas
248
+ const quotas = await makemind.settlement.getQuotaStatus();
249
+
250
+ // List invoices
251
+ const invoices = await makemind.settlement.listInvoices({ status: 'pending' });
252
+ ```
253
+
254
+ ## Storage
255
+
256
+ ```typescript
257
+ const makemind = getMAKEMIND();
258
+
259
+ // Upload a file
260
+ const uploaded = await makemind.storage.upload({
261
+ path: 'products/product-id/documents/spec.pdf',
262
+ file: fileFromInput, // File | Blob
263
+ metadata: { documentType: 'spec' },
264
+ onProgress: (progress) => console.log(`${progress.toFixed(0)}%`),
265
+ });
266
+
267
+ // Get a download URL / download directly
268
+ const url = await makemind.storage.getDownloadUrl('products/product-id/documents/spec.pdf');
269
+ const blob = await makemind.storage.download('products/product-id/documents/spec.pdf');
270
+
271
+ // List, copy, move, delete
272
+ const { files } = await makemind.storage.listFiles({ prefix: 'products/product-id/documents/' });
273
+ await makemind.storage.copy('a.pdf', 'b.pdf');
274
+ await makemind.storage.move('a.pdf', 'archive/a.pdf');
275
+ await makemind.storage.delete('a.pdf');
276
+
277
+ // PLM-specific helpers
278
+ await makemind.storage.uploadProductDocument('product-id', file, { documentType: 'drawing' });
279
+ await makemind.storage.uploadEcoAttachment('eco-id', file);
280
+ await makemind.storage.uploadQualityAttachment('issue-id', file);
281
+ const productDocs = await makemind.storage.getProductDocuments('product-id');
282
+
283
+ // Bucket management
284
+ const buckets = await makemind.storage.listBuckets();
285
+ await makemind.storage.createBucket({ name: 'reports', publicAccess: false });
286
+ const stats = await makemind.storage.getUsageStats();
287
+ ```
288
+
289
+ ## AI
290
+
291
+ ```typescript
292
+ const makemind = getMAKEMIND();
293
+
294
+ // Chat completion
295
+ const { message, usage } = await makemind.ai.chat([
296
+ { role: 'user', content: 'Summarize this ECO for a release note.' },
297
+ ]);
298
+
299
+ // Streaming (polling-based; delivers content deltas as they arrive)
300
+ await makemind.ai.chatStream(
301
+ [{ role: 'user', content: 'Draft a quality report.' }],
302
+ (chunk) => process.stdout.write(chunk),
303
+ { onDone: (response) => console.log('done', response.usage) }
304
+ );
305
+
306
+ // Or consume as an async generator
307
+ for await (const delta of makemind.ai.streamChat([{ role: 'user', content: 'Hi' }])) {
308
+ console.log(delta);
309
+ }
310
+
311
+ // Embeddings
312
+ const { embeddings } = await makemind.ai.createEmbedding(['spec text', 'another doc']);
313
+
314
+ // Vector search over a tenant knowledge collection
315
+ const hits = await makemind.ai.vectorSearch('specs', 'torque tolerance', { limit: 5 });
316
+
317
+ // Vision
318
+ await makemind.ai.analyzeImage('https://example.com/part.png', 'Describe this part.');
319
+ await makemind.ai.analyzeImageBase64(base64Data, 'image/png', 'Describe this part.');
320
+
321
+ // Tool calling
322
+ const { toolCalls } = await makemind.ai.chatWithTools(
323
+ [{ role: 'user', content: 'Look up product PRD-001.' }],
324
+ [{ name: 'getProduct', description: 'Fetch a product by code', parameters: { type: 'object' } }]
325
+ );
326
+
327
+ // Convenience helpers
328
+ const { text } = await makemind.ai.generate('Write a one-line changelog entry.');
329
+ const { summary } = await makemind.ai.summarize(longText, { style: 'bullet', maxLength: 100 });
330
+ const { translation } = await makemind.ai.translate('Hello world', 'ko');
331
+ ```
332
+
333
+ ## Search
334
+
335
+ ```typescript
336
+ const makemind = getMAKEMIND();
337
+
338
+ // Simple query
339
+ const results = await makemind.search.query({ q: 'bracket', collections: ['plm_products'], perPage: 20 });
340
+
341
+ // Advanced query with facets/filters/highlighting
342
+ const advanced = await makemind.search.advanced({
343
+ query: 'bracket',
344
+ collection: 'plm_products',
345
+ facets: ['status', 'category'],
346
+ filters: { status: 'active' },
347
+ highlightFields: ['product_name'],
348
+ });
349
+
350
+ // Autocomplete suggestions
351
+ const suggestions = await makemind.search.suggestions({ prefix: 'brac', collection: 'plm_products' });
352
+
353
+ // Analytics and reindexing
354
+ const analytics = await makemind.search.getAnalytics({ startDate: '2026-01-01' });
355
+ const { jobId } = await makemind.search.bulkReindex({ collection: 'plm_products' });
356
+ const status = await makemind.search.getBulkReindexStatus({ jobId });
357
+ ```
358
+
359
+ ## Apps (App Builder)
360
+
361
+ ```typescript
362
+ const makemind = getMAKEMIND();
363
+
364
+ // Create and manage apps
365
+ const { appId } = await makemind.apps.createApp({ name: 'field-service', displayName: 'Field Service' });
366
+ const app = await makemind.apps.getApp(appId);
367
+ const { apps } = await makemind.apps.listApps({ status: 'draft' });
368
+ await makemind.apps.updateApp(appId, { settings: { theme: { mode: 'dark' } } });
369
+
370
+ // Pages
371
+ const { pageId } = await makemind.apps.createPage(appId, { name: 'Home', path: '/', isHomePage: true });
372
+ await makemind.apps.updatePage(appId, pageId, { title: 'Home' });
373
+ const pages = await makemind.apps.listPages(appId);
374
+
375
+ // Publishing & versioning
376
+ await makemind.apps.publish(appId, 'Initial release');
377
+ const versions = await makemind.apps.getVersions(appId);
378
+ await makemind.apps.rollback(appId, versions[0].version);
379
+
380
+ // Domains
381
+ await makemind.apps.addSubdomain(appId, 'field-service');
382
+ const { verificationToken, dnsRecords } = await makemind.apps.addCustomDomain(appId, 'app.example.com');
383
+
384
+ // AI-assisted UI generation
385
+ const { components } = await makemind.apps.generateUI('A form to log a quality issue');
386
+
387
+ // Live-edit collaboration
388
+ const { sessionToken, wsEndpoint } = await makemind.apps.startSession(appId, pageId);
389
+ ```
390
+
391
+ ## Marketplace
392
+
393
+ ```typescript
394
+ const makemind = getMAKEMIND();
395
+
396
+ // Discovery
397
+ const results = await makemind.marketplace.search({ query: 'inventory', category: 'operations' });
398
+ const listing = await makemind.marketplace.getDetail('listing-id');
399
+ const featured = await makemind.marketplace.getFeatured();
400
+
401
+ // Installation
402
+ const { bindingId } = await makemind.marketplace.install('listing-id');
403
+ const installed = await makemind.marketplace.listInstalled();
404
+ await makemind.marketplace.uninstall('listing-id');
405
+
406
+ // App runtime (proxy calls through the installed binding)
407
+ const config = await makemind.marketplace.getConfig(bindingId);
408
+ await makemind.marketplace.proxy(bindingId, { method: 'GET', path: 'items' });
409
+
410
+ // Reviews
411
+ await makemind.marketplace.createReview({ listingId: 'listing-id', rating: 5, body: 'Great app' });
412
+ const reviews = await makemind.marketplace.listReviews('listing-id');
413
+
414
+ // Publishing (for publisher tenants)
415
+ await makemind.marketplace.createPublisherProfile({
416
+ displayName: 'Acme Apps',
417
+ description: 'Field service tools',
418
+ logoUrl: 'https://example.com/logo.png',
419
+ supportEmail: 'support@acme.example',
420
+ });
421
+ const newListing = await makemind.marketplace.createListing({ name: 'Inventory Sync' });
422
+ await makemind.marketplace.submitForReview((newListing as { listingId: string }).listingId);
423
+ ```
424
+
425
+ ## Platform Operations (Generic Dispatch)
426
+
427
+ Every typed service method (`data.*`, `tenant.*`, `messaging.*`, ...) is a thin
428
+ wrapper around the gateway's RPC dispatch route. When a backend operation does
429
+ not yet have a typed service method, call it directly with `makemind.call()`:
430
+
431
+ ```typescript
432
+ const makemind = getMAKEMIND();
433
+
434
+ // call<T>(operation, input) -> POST /api/v<version>/call/:operation
435
+ // The `{ data }` response envelope is already unwrapped.
436
+ const result = await makemind.call('updateMyProfile', { displayName: 'Jane Doe' });
437
+ ```
438
+
439
+ `call(operation, input)` invokes a platform Callable operation through the
440
+ gateway RPC dispatch route. The operation key is the deployed Callable name:
441
+ namespaced groups use `<prefix>-<name>` (e.g. `settlement-getMRR`,
442
+ `marketplace-listPublisherListings`); flat exports use the bare name (e.g.
443
+ `createTenant`, `updateMyProfile`). Each operation enforces its own
444
+ authorization server-side (platform roles / tenant scopes), so calling an
445
+ operation you are not authorized for still fails with `PERMISSION_DENIED` —
446
+ `call()` does not bypass access control.
447
+
448
+ This is distinct from `logic.call(functionName, input)`, which executes a
449
+ tenant-defined *logic* function (`POST /logic/call`), not a platform Callable.
450
+
451
+ Some illustrative operations reachable only through `call()` (no dedicated
452
+ typed method yet):
453
+
454
+ ```typescript
455
+ await makemind.call('platformGetAuditLogs', { limit: 50 });
456
+ await makemind.call('updateMyProfile', { displayName: 'Jane Doe' });
457
+ await makemind.call('endusers-listEndUsers', { limit: 20 });
458
+ await makemind.call('knowledge-knowledgeCreateUploadUrl', { fileName: 'spec.pdf' });
459
+ await makemind.call('getBillingContact', {});
460
+ await makemind.call('updateBillingContact', { email: 'billing@example.com' });
461
+ await makemind.call('getMyLoginHistory', { limit: 10 });
462
+ await makemind.call('createBrandingLogoUploadUrl', { fileName: 'logo.png' });
463
+ await makemind.call('marketplace-listPublisherListings', {});
464
+ ```
465
+
466
+ ## React Integration Example
467
+
468
+ ```tsx
469
+ // src/lib/makemind.ts
470
+ import { initializeMAKEMIND } from '@makemind/sdk';
471
+
472
+ export const makemind = initializeMAKEMIND({
473
+ apiKey: import.meta.env.VITE_MAKEMIND_API_KEY,
474
+ applicationId: 'plm',
475
+ environment: import.meta.env.DEV ? 'development' : 'production',
476
+ baseUrl: import.meta.env.VITE_MAKEMIND_BASE_URL, // optional gateway override
477
+ });
478
+
479
+ // src/hooks/useProducts.ts
480
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
481
+ import { getMAKEMIND } from '@makemind/sdk';
482
+
483
+ export function useProducts() {
484
+ const makemind = getMAKEMIND();
485
+
486
+ return useQuery({
487
+ queryKey: ['plm_products', makemind.tenantId],
488
+ queryFn: async () => {
489
+ const result = await makemind.data.query({
490
+ collection: 'plm_products',
491
+ where: [
492
+ { field: 'tenant_id', op: '==', value: makemind.tenantId! },
493
+ ],
494
+ orderBy: [{ field: 'updated_at', direction: 'desc' }],
495
+ });
496
+ return result.data;
497
+ },
498
+ enabled: !!makemind.tenantId,
499
+ });
500
+ }
501
+
502
+ export function useCreateProduct() {
503
+ const queryClient = useQueryClient();
504
+ const makemind = getMAKEMIND();
505
+
506
+ return useMutation({
507
+ mutationFn: async (data: CreateProductInput) => {
508
+ const result = await makemind.data.create({
509
+ collection: 'plm_products',
510
+ data,
511
+ });
512
+
513
+ // Record usage
514
+ await makemind.settlement.recordProductCreation(result.id);
515
+
516
+ return result;
517
+ },
518
+ onSuccess: () => {
519
+ queryClient.invalidateQueries({ queryKey: ['plm_products'] });
520
+ },
521
+ });
522
+ }
523
+ ```
524
+
525
+ ## Error Handling
526
+
527
+ ```typescript
528
+ import { MAKEMINDError, ErrorCodes } from '@makemind/sdk';
529
+
530
+ try {
531
+ await makemind.data.create({ ... });
532
+ } catch (error) {
533
+ if (error instanceof MAKEMINDError) {
534
+ switch (error.code) {
535
+ case ErrorCodes.PERMISSION_DENIED:
536
+ console.error('Access denied');
537
+ break;
538
+ case ErrorCodes.QUOTA_EXCEEDED:
539
+ console.error('Quota exceeded');
540
+ break;
541
+ default:
542
+ console.error('Error:', error.message);
543
+ }
544
+ }
545
+ }
546
+ ```
547
+
548
+ ## License
549
+
550
+ MIT