@nextplus/js-sdk 0.0.11 → 1.14.46

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.
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/smoke.js ADDED
@@ -0,0 +1,79 @@
1
+ import {
2
+ configure
3
+ } from "./chunk-SUTLUR6L.js";
4
+ import "./chunk-UNVS4USV.js";
5
+ import "./chunk-SFE3EXIE.js";
6
+ import "./chunk-FXXZNC42.js";
7
+
8
+ // src/smoke.ts
9
+ async function main() {
10
+ console.log("=== Testing with email and returnType: data ===");
11
+ const sdk = configure({
12
+ baseURL: "http://127.0.0.1:3000/api",
13
+ email: "operator@operator.com",
14
+ password: "operator@operator.com",
15
+ returnType: "data"
16
+ // Only return data, not full response
17
+ });
18
+ console.log("SDK configured (email)");
19
+ const tables = await sdk.TableService.find({
20
+ query: {
21
+ filter: JSON.stringify({
22
+ language: "he",
23
+ limit: 10,
24
+ order: "created DESC",
25
+ fields: {
26
+ id: true,
27
+ name: true,
28
+ created: true
29
+ },
30
+ where: {
31
+ name: "no-such-table!"
32
+ }
33
+ })
34
+ }
35
+ });
36
+ console.log(
37
+ "Tables found (data only):",
38
+ Array.isArray(tables) ? `Array with ${tables.length} items` : tables
39
+ );
40
+ try {
41
+ const newTable = await sdk.TableService.create({ body: {} });
42
+ console.log("New table created:", newTable);
43
+ } catch (error) {
44
+ console.log("Expected validation error caught:", error.message);
45
+ }
46
+ console.log("\n=== Testing with username and returnType: raw ===");
47
+ const sdk2 = configure({
48
+ baseURL: "http://127.0.0.1:3000/api",
49
+ username: "operator",
50
+ // Using username instead of email
51
+ password: "operator@operator.com",
52
+ returnType: "raw"
53
+ // Return full response object
54
+ });
55
+ console.log("SDK configured (username)");
56
+ const tablesRaw = await sdk2.TableService.find({
57
+ query: {
58
+ filter: JSON.stringify({
59
+ where: {
60
+ name: "test"
61
+ },
62
+ limit: 5
63
+ })
64
+ }
65
+ });
66
+ console.log("Tables found (raw response):", {
67
+ hasData: "data" in tablesRaw,
68
+ hasRequest: "request" in tablesRaw,
69
+ hasResponse: "response" in tablesRaw,
70
+ dataLength: tablesRaw.data ? tablesRaw.data.length : "no data"
71
+ });
72
+ try {
73
+ const createTable = await sdk2.TableService.create({ body: {} });
74
+ console.log("Table created:", createTable);
75
+ } catch (error) {
76
+ console.log("Expected validation error caught (raw mode):", error.message);
77
+ }
78
+ }
79
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,18 +1,16 @@
1
1
  {
2
2
  "name": "@nextplus/js-sdk",
3
- "version": "0.0.11",
3
+ "version": "1.14.46",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "generate": "npm run clean && npm run prepare-spec && npm run openapi-ts && npm run build",
7
- "build": "tsup",
7
+ "build": "tsup src/index.ts src/sdk/index.ts src/sdk/client/index.ts src/sdk/sdk.gen.ts src/smoke.ts --dts --format esm,cjs --outDir dist --clean",
8
8
  "clean": "rm -rf dist && rm -rf .generated",
9
- "prepublishOnly": "npm run clean && SKIP_SMOKE=1 tsup",
9
+ "prepublishOnly": "npm run clean && tsup src/index.ts src/sdk/index.ts src/sdk/client/index.ts src/sdk/sdk.gen.ts --dts --format esm,cjs --outDir dist --clean",
10
10
  "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
11
11
  "openapi-ts": "openapi-ts",
12
12
  "prepare-spec": "node scripts/prepare-spec.js",
13
- "test": "npm run build && node --test dist/smoke.js",
14
- "test:watch": "tsx watch --test src/smoke.ts",
15
- "test:dev": "tsx --test src/smoke.ts"
13
+ "test": "npm run build && node dist/smoke.js"
16
14
  },
17
15
  "files": [
18
16
  "dist",
@@ -26,11 +24,6 @@
26
24
  "import": "./dist/index.js",
27
25
  "require": "./dist/index.cjs"
28
26
  },
29
- "./services": {
30
- "types": "./dist/sdk/sdk.gen.d.ts",
31
- "import": "./dist/sdk/sdk.gen.js",
32
- "require": "./dist/sdk/sdk.gen.cjs"
33
- },
34
27
  "./sdk": {
35
28
  "types": "./dist/sdk/index.d.ts",
36
29
  "import": "./dist/sdk/index.js",
@@ -59,7 +52,6 @@
59
52
  "eslint": "^9.32.0",
60
53
  "prettier": "^3.6.2",
61
54
  "tsup": "^8.5.0",
62
- "tsx": "^4.21.0",
63
55
  "typescript": "^5.9.2"
64
56
  },
65
57
  "dependencies": {
package/src/index.ts CHANGED
@@ -1,43 +1,18 @@
1
1
  import debug from 'debug';
2
- import { createClient, createConfig } from './sdk/client';
2
+ import { createClient } from './sdk/client';
3
3
  import { UserModelService } from './sdk';
4
4
  import * as AllServices from './sdk';
5
5
  import type * as AllServiceTypes from './sdk';
6
6
 
7
7
  const log = debug('nextplus:sdk');
8
8
 
9
- // Configuration types for token-based authentication
10
- export type TokenConfig = {
11
- baseURL?: string;
12
- accessToken: string;
13
- returnType?: 'raw' | 'data';
14
- throwOnError?: boolean; // For tree-shakeable imports
15
- };
16
-
17
- // Configuration types for credential-based authentication
18
- export type CredentialConfig = {
19
- baseURL?: string;
20
- email?: string;
21
- username?: string;
22
- password: string;
23
- returnType?: 'raw' | 'data';
24
- throwOnError?: boolean; // For tree-shakeable imports
25
- };
26
-
27
- // Union type for SDK configuration
28
- export type SDKConfig = TokenConfig | CredentialConfig;
29
-
30
9
  const accessToken = {
31
10
  token: null as string | null,
32
11
  created: null as Date | null,
33
12
  ttl: 0,
34
13
  };
35
14
 
36
- const setAccessToken = (
37
- token: string,
38
- created: Date = new Date(),
39
- ttl: number = 1209600,
40
- ) => {
15
+ const setAccessToken = (token: string, created: Date = new Date(), ttl: number = 1209600) => {
41
16
  accessToken.token = token;
42
17
  accessToken.created = created;
43
18
  accessToken.ttl = ttl;
@@ -59,18 +34,17 @@ const isTokenValid = (): boolean => {
59
34
  return tokenAge < tokenTtlMs;
60
35
  };
61
36
 
37
+
38
+
62
39
  export class NextPlusSDK {
63
- public client: any; // Exposed for tree-shakeable service imports
64
- private credentials?: { email?: string; username?: string; password: string };
65
- private providedAccessToken?: string;
40
+ private client: any;
41
+ private credentials: { email?: string; username?: string; password: string };
66
42
  private returnType: 'raw' | 'data' = 'data';
67
- private authMode: 'token' | 'credentials';
68
43
 
69
44
  // Wrap plain developer inputs into the right container (body/query)
70
45
  // and stringify complex query params when required by LoopBack (filter/where)
71
46
  private normalizeOptions(methodName: string, rawOptions: any): any {
72
- const options: any =
73
- rawOptions && typeof rawOptions === 'object' ? { ...rawOptions } : {};
47
+ const options: any = rawOptions && typeof rawOptions === 'object' ? { ...rawOptions } : {};
74
48
 
75
49
  // Always ensure client is set (will be set by caller afterwards if missing)
76
50
  // We avoid mutating the original object to keep predictable behavior
@@ -80,22 +54,10 @@ export class NextPlusSDK {
80
54
  const method = methodName.toString().toLowerCase();
81
55
  const isFind = method.includes('find') && !method.includes('count');
82
56
  const isCount = method.includes('count');
83
- const shouldUseBody = /(create|update|replace|patch|login|upsert)/i.test(
84
- methodName.toString(),
85
- );
57
+ const shouldUseBody = /(create|update|replace|patch|login|upsert)/i.test(methodName.toString());
86
58
 
87
- const topLevelKeys = [
88
- 'filter',
89
- 'where',
90
- 'fields',
91
- 'order',
92
- 'limit',
93
- 'offset',
94
- 'include',
95
- ];
96
- const hasTopLevelFilterBits = topLevelKeys.some((k) =>
97
- Object.prototype.hasOwnProperty.call(options, k),
98
- );
59
+ const topLevelKeys = ['filter', 'where', 'fields', 'order', 'limit', 'offset', 'include'];
60
+ const hasTopLevelFilterBits = topLevelKeys.some((k) => Object.prototype.hasOwnProperty.call(options, k));
99
61
 
100
62
  // Only create query if relevant for this method
101
63
  if (isFind) {
@@ -111,13 +73,9 @@ export class NextPlusSDK {
111
73
  if (options.order !== undefined) filterObject.order = options.order;
112
74
  if (options.limit !== undefined) filterObject.limit = options.limit;
113
75
  if (options.offset !== undefined) filterObject.offset = options.offset;
114
- if (options.include !== undefined)
115
- filterObject.include = options.include;
76
+ if (options.include !== undefined) filterObject.include = options.include;
116
77
  if (Object.keys(filterObject).length > 0) {
117
- options.query.filter =
118
- typeof filterObject === 'string'
119
- ? filterObject
120
- : JSON.stringify(filterObject);
78
+ options.query.filter = typeof filterObject === 'string' ? filterObject : JSON.stringify(filterObject);
121
79
  }
122
80
  // Clean moved keys from top-level
123
81
  topLevelKeys.forEach((k) => delete options[k]);
@@ -127,8 +85,7 @@ export class NextPlusSDK {
127
85
  // Allow top-level where or query.where as object → stringify
128
86
  if (options.where !== undefined) {
129
87
  const whereVal = options.where;
130
- options.query.where =
131
- typeof whereVal === 'string' ? whereVal : JSON.stringify(whereVal);
88
+ options.query.where = typeof whereVal === 'string' ? whereVal : JSON.stringify(whereVal);
132
89
  delete options.where;
133
90
  }
134
91
  } else if (shouldUseBody) {
@@ -142,8 +99,7 @@ export class NextPlusSDK {
142
99
  options.body = bodyCandidate;
143
100
  // Keep only body/client/query/path at root
144
101
  Object.keys(options).forEach((k) => {
145
- if (!['body', 'client', 'query', 'path'].includes(k))
146
- delete options[k];
102
+ if (!['body', 'client', 'query', 'path'].includes(k)) delete options[k];
147
103
  });
148
104
  }
149
105
  }
@@ -162,83 +118,41 @@ export class NextPlusSDK {
162
118
  return options;
163
119
  }
164
120
 
165
- constructor(options: SDKConfig) {
166
- // Determine authentication mode
167
- const hasToken = 'accessToken' in options && options.accessToken;
168
- const hasPassword = 'password' in options && options.password;
169
-
170
- // Validate mutually exclusive authentication
171
- if (hasToken && hasPassword) {
172
- throw new Error(
173
- 'Provide either accessToken or credentials (email/username + password), not both',
174
- );
121
+ constructor(options: {
122
+ baseURL: string;
123
+ email?: string;
124
+ username?: string;
125
+ password: string;
126
+ returnType?: 'raw' | 'data';
127
+ }) {
128
+ // Validate that either email or username is provided
129
+ if (!options.email && !options.username) {
130
+ throw new Error('Either email or username must be provided');
175
131
  }
176
-
177
- if (!hasToken && !hasPassword) {
178
- throw new Error(
179
- 'Either accessToken or credentials (email/username + password) must be provided',
180
- );
181
- }
182
-
183
- // Set authentication mode
184
- if (hasToken) {
185
- this.authMode = 'token';
186
- this.providedAccessToken = (options as TokenConfig).accessToken;
187
- // Set the token immediately for token-based auth
188
- setAccessToken(this.providedAccessToken, new Date(), 1209600);
189
- } else {
190
- this.authMode = 'credentials';
191
- const credConfig = options as CredentialConfig;
192
-
193
- // Validate that either email or username is provided
194
- if (!credConfig.email && !credConfig.username) {
195
- throw new Error(
196
- 'Either email or username must be provided with password',
197
- );
198
- }
199
- if (credConfig.email && credConfig.username) {
200
- throw new Error('Provide either email or username, not both');
201
- }
202
-
203
- this.credentials = {
204
- email: credConfig.email,
205
- username: credConfig.username,
206
- password: credConfig.password,
207
- };
132
+ if (options.email && options.username) {
133
+ throw new Error('Provide either email or username, not both');
208
134
  }
209
135
 
136
+ this.credentials = {
137
+ email: options.email,
138
+ username: options.username,
139
+ password: options.password
140
+ };
210
141
  this.returnType = options.returnType || 'data';
211
-
212
- // Handle baseURL with smart defaults
213
- let baseURL = options.baseURL;
214
- if (!baseURL) {
215
- // Try to detect browser environment
216
- if (typeof window !== 'undefined' && window.location) {
217
- baseURL = window.location.origin + '/api';
218
- } else {
219
- throw new Error('baseURL is required in non-browser environments');
220
- }
221
- }
222
-
223
- // Normalize baseURL
224
- if (baseURL.endsWith('/')) {
225
- baseURL = baseURL.slice(0, -1);
142
+ // remove trailing slash from baseURL
143
+ if (options.baseURL.endsWith('/')) {
144
+ options.baseURL = options.baseURL.slice(0, -1);
226
145
  }
227
- if (!baseURL.endsWith('/api')) {
228
- baseURL = baseURL + '/api';
146
+ // if baseURL does not end with /api, add it
147
+ if (!options.baseURL.endsWith('/api')) {
148
+ options.baseURL = options.baseURL + '/api';
229
149
  }
230
-
231
- this.client = createClient(
232
- createConfig({
233
- baseUrl: baseURL,
234
- throwOnError: options.throwOnError ?? false,
235
- }),
236
- );
150
+ this.client = createClient({
151
+ baseUrl: options.baseURL,
152
+ });
237
153
 
238
154
  // Add authentication interceptor
239
- this.client.interceptors.request.use(
240
- this.authRequestInterceptor.bind(this),
241
- );
155
+ this.client.interceptors.request.use(this.authRequestInterceptor.bind(this));
242
156
 
243
157
  // Create service proxies dynamically
244
158
  this.createServiceProxies();
@@ -250,37 +164,24 @@ export class NextPlusSDK {
250
164
  return request;
251
165
  }
252
166
 
253
- // Handle authentication based on mode
254
- if (this.authMode === 'token') {
255
- // For token mode, use the provided token directly
256
- if (this.providedAccessToken) {
257
- request.headers.set('Authorization', this.providedAccessToken);
258
- }
259
- } else {
260
- // For credentials mode, ensure we have a valid token
261
- await this.ensureValidToken();
262
- const token = getAccessToken();
263
- if (token.token) {
264
- request.headers.set('Authorization', token.token);
265
- }
266
- }
167
+ // Ensure we have a valid token
168
+ await this.ensureValidToken();
267
169
 
170
+ const token = getAccessToken();
171
+ if (token.token) {
172
+ request.headers.set('Authorization', token.token);
173
+ }
268
174
  return request;
269
175
  }
270
176
 
271
177
  private async ensureValidToken(): Promise<void> {
272
- // Only validate token for credentials mode
273
- if (this.authMode === 'credentials' && !isTokenValid()) {
178
+ if (!isTokenValid()) {
274
179
  log('Token expired or missing, refreshing...');
275
180
  await this.login();
276
181
  }
277
182
  }
278
183
 
279
184
  private async login(): Promise<boolean> {
280
- if (this.authMode !== 'credentials' || !this.credentials) {
281
- throw new Error('Login is only available in credentials mode');
282
- }
283
-
284
185
  try {
285
186
  const loginBody: any = {
286
187
  password: this.credentials.password,
@@ -318,13 +219,11 @@ export class NextPlusSDK {
318
219
 
319
220
  private createServiceProxies() {
320
221
  // Get all service classes from the SDK
321
- const serviceNames = Object.keys(AllServices).filter(
322
- (key) =>
323
- key.endsWith('Service') &&
324
- typeof (AllServices as any)[key] === 'function',
222
+ const serviceNames = Object.keys(AllServices).filter(key =>
223
+ key.endsWith('Service') && typeof (AllServices as any)[key] === 'function'
325
224
  );
326
225
 
327
- serviceNames.forEach((serviceName) => {
226
+ serviceNames.forEach(serviceName => {
328
227
  const ServiceClass = (AllServices as any)[serviceName];
329
228
 
330
229
  // Create a proxy for each service
@@ -337,7 +236,7 @@ export class NextPlusSDK {
337
236
  return new Proxy(ServiceClass, {
338
237
  get(target, prop) {
339
238
  if (typeof target[prop] === 'function') {
340
- return async function (...args: any[]) {
239
+ return async function(...args: any[]) {
341
240
  // Automatically inject client if not provided
342
241
  if (!args[0]) args[0] = {};
343
242
  // Normalize developer-friendly input into OpenAPI options
@@ -347,25 +246,11 @@ export class NextPlusSDK {
347
246
  const result = await target[prop].apply(target, args);
348
247
 
349
248
  // Return based on returnType setting
350
- if (
351
- self.returnType === 'data' &&
352
- result &&
353
- typeof result === 'object' &&
354
- 'data' in result
355
- ) {
249
+ if (self.returnType === 'data' && result && typeof result === 'object' && 'data' in result) {
356
250
  return result.data;
357
251
  }
358
- if (
359
- self.returnType === 'data' &&
360
- result &&
361
- typeof result === 'object' &&
362
- 'error' in result
363
- ) {
364
- const error = new Error(
365
- result.error.error.message ||
366
- result.error.error.code ||
367
- 'Unknown error',
368
- ) as any;
252
+ if (self.returnType === 'data' && result && typeof result === 'object' && 'error' in result) {
253
+ const error = new Error(result.error.error.message || result.error.error.code || 'Unknown error') as any;
369
254
  if (result.error.error.details) {
370
255
  error.details = result.error.error.details;
371
256
  }
@@ -376,111 +261,46 @@ export class NextPlusSDK {
376
261
  };
377
262
  }
378
263
  return target[prop];
379
- },
264
+ }
380
265
  });
381
266
  }
382
267
 
383
- // Manual login method (optional, for explicit control in credentials mode)
268
+ // Manual login method (optional, for explicit control)
384
269
  async manualLogin(): Promise<boolean> {
385
- if (this.authMode !== 'credentials') {
386
- throw new Error('manualLogin is only available in credentials mode');
387
- }
388
270
  return this.login();
389
271
  }
390
-
391
- // Bind a service to this SDK's client for tree-shakeable imports
392
- // This eliminates the need to pass client on every call
393
- service<T extends Record<string, any>>(ServiceClass: T): T {
394
- const client = this.client;
395
- const boundService = Object.create(ServiceClass);
396
-
397
- // Get all property names including static methods
398
- const properties = Object.getOwnPropertyNames(ServiceClass);
399
-
400
- properties.forEach((key) => {
401
- const descriptor = Object.getOwnPropertyDescriptor(ServiceClass, key);
402
- if (
403
- descriptor &&
404
- typeof descriptor.value === 'function' &&
405
- key !== 'constructor'
406
- ) {
407
- // Bind static methods to auto-inject client
408
- boundService[key] = (options: any = {}) => {
409
- return descriptor.value.call(ServiceClass, { ...options, client });
410
- };
411
- }
412
- });
413
-
414
- return boundService;
415
- }
416
272
  }
417
273
 
418
274
  // Expose accurate static service method types on the SDK instance for IDE/type safety
419
275
  type ServiceStatics = {
420
- [K in keyof typeof AllServiceTypes as K extends `${string}Service`
421
- ? K
422
- : never]: (typeof AllServiceTypes)[K];
276
+ [K in keyof typeof AllServiceTypes as K extends `${string}Service` ? K : never]: typeof AllServiceTypes[K];
423
277
  };
424
278
 
425
279
  export interface NextPlusSDK extends ServiceStatics {}
426
280
 
427
- // SDK instance type
281
+ // Legacy function for backward compatibility
428
282
  type SDKInstance = NextPlusSDK & ServiceStatics;
429
283
 
430
- // Import the generated default client to update it
431
- import { client as generatedClient } from './sdk/client.gen';
432
-
433
- // Configure function - creates a full SDK instance with service proxies
434
- // Automatically sets the client as global default for tree-shakeable imports
435
- const configure = (options: SDKConfig): SDKInstance => {
436
- const sdk = new NextPlusSDK(options) as SDKInstance;
437
- // Automatically set as global default
438
- setDefaultClient(sdk.client);
439
- // Update the generated SDK's default client so services work without passing client
440
- Object.assign(generatedClient, sdk.client);
441
- return sdk;
284
+ const configure = (options: {
285
+ baseURL: string;
286
+ email?: string;
287
+ username?: string;
288
+ password: string;
289
+ returnType?: 'raw' | 'data';
290
+ }): SDKInstance => {
291
+ return new NextPlusSDK(options) as SDKInstance;
442
292
  };
443
293
 
444
- // Create SDK instance - alias for configure
445
- const createSDK = (options: SDKConfig): SDKInstance => {
446
- const sdk = new NextPlusSDK(options) as SDKInstance;
447
- // Automatically set as global default
448
- setDefaultClient(sdk.client);
449
- // Update the generated SDK's default client so services work without passing client
450
- Object.assign(generatedClient, sdk.client);
451
- return sdk;
294
+ // Create SDK instance
295
+ const createSDK = (options: {
296
+ baseURL: string;
297
+ email?: string;
298
+ username?: string;
299
+ password: string;
300
+ returnType?: 'raw' | 'data';
301
+ }): SDKInstance => {
302
+ return new NextPlusSDK(options) as SDKInstance;
452
303
  };
453
304
 
454
- // Global default client for tree-shakeable imports
455
- // Automatically set when configure() or createSDK() is called
456
- let globalClient: any = null;
457
-
458
- /**
459
- * Set a global default client for all service calls
460
- * Note: This is automatically called by configure() and createSDK()
461
- * You usually don't need to call this manually
462
- */
463
- export function setDefaultClient(client: any): void {
464
- globalClient = client;
465
- }
466
-
467
- /**
468
- * Get the current global default client
469
- */
470
- export function getDefaultClient(): any {
471
- return globalClient;
472
- }
473
-
474
- /**
475
- * Clear the global default client (useful for logout)
476
- */
477
- export function clearDefaultClient(): void {
478
- globalClient = null;
479
- }
480
-
481
305
  export type { SDKInstance };
482
306
  export { configure, createSDK };
483
-
484
- // Re-export SDK types and generated services
485
- // Services will automatically use global client as fallback via the generated code's default client
486
- export * from './sdk';