zone4code-sdk 1.0.8 → 1.0.9

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/dist/index.d.cts CHANGED
@@ -42,7 +42,17 @@ interface Zone4CodeConfig {
42
42
  * Callback invoked when a user session expires (e.g. refresh token expired or invalid).
43
43
  */
44
44
  onSessionExpired?: () => void;
45
+ /**
46
+ * Default language locale for API requests (e.g. 'en', 'fr', 'ar').
47
+ * Can be overridden per query with `.lang(locale)` or set to '*' for raw translations.
48
+ */
49
+ defaultLanguage?: string;
45
50
  }
51
+ /**
52
+ * Represents either a plain localized string or a dictionary of multilingual values:
53
+ * e.g. "Beach Villa" or { en: "Beach Villa", fr: "Villa Plage", ar: "فيلا الشاطئ" }
54
+ */
55
+ type Translatable<T = string> = T | Record<string, T>;
46
56
  interface AuthUser {
47
57
  id: string;
48
58
  email?: string;
@@ -77,6 +87,12 @@ interface EntityListResponse<T = Record<string, any>> {
77
87
  limit?: number;
78
88
  cursor?: string;
79
89
  }
90
+ interface GetEntityOptions {
91
+ flat?: boolean;
92
+ include?: string[];
93
+ lang?: string;
94
+ rawTranslations?: boolean;
95
+ }
80
96
  interface SchemaProperty {
81
97
  type: 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array';
82
98
  description?: string;
@@ -85,6 +101,11 @@ interface SchemaProperty {
85
101
  items?: SchemaProperty;
86
102
  properties?: Record<string, SchemaProperty>;
87
103
  required?: string[];
104
+ /**
105
+ * If true, this field accepts multilingual JSONB dictionaries ({ en: "...", fr: "..." })
106
+ * and automatically flattens to the active language during queries.
107
+ */
108
+ translatable?: boolean;
88
109
  }
89
110
  interface EntitySchemaDefinition {
90
111
  typeName: string;
@@ -159,6 +180,7 @@ declare class AuthClient {
159
180
  private sessionExpiredListeners;
160
181
  private activeRefreshPromise;
161
182
  private storageLoadedPromise;
183
+ private getLanguage?;
162
184
  private static TOKEN_KEY_PREFIX;
163
185
  constructor(options: {
164
186
  gatewayUrl: string;
@@ -170,6 +192,7 @@ declare class AuthClient {
170
192
  autoRefresh?: boolean;
171
193
  tokenExpiryBuffer?: number;
172
194
  onSessionExpired?: () => void;
195
+ getLanguage?: () => string | null;
173
196
  });
174
197
  getToken(): string | null;
175
198
  getRefreshToken(): string | null;
@@ -287,6 +310,7 @@ declare class EntityQueryBuilder<T = Record<string, any>> {
287
310
  getToken?: () => string | null;
288
311
  fetchFn?: typeof fetch;
289
312
  fetchWithAuth?: (url: string, init?: RequestInit) => Promise<Response>;
313
+ defaultLanguage?: string | null | (() => string | null);
290
314
  });
291
315
  private getHeaders;
292
316
  private executeRequest;
@@ -335,17 +359,29 @@ declare class EntityQueryBuilder<T = Record<string, any>> {
335
359
  offset(offset: number): this;
336
360
  cursor(token: string): this;
337
361
  flat(enabled?: boolean): this;
362
+ /**
363
+ * Set target language for automatic JSONB translation flattening.
364
+ * Example: .lang('fr') returns French fields, falling back to 'en'.
365
+ * Set to '*' or call .rawTranslations() to get raw multilingual dictionaries.
366
+ */
367
+ lang(locale: string): this;
368
+ /**
369
+ * Alias for .lang(locale)
370
+ */
371
+ language(locale: string): this;
372
+ /**
373
+ * Request raw, unflattened multilingual dictionaries ({ en: "...", fr: "..." })
374
+ * Essential for Admin/CMS forms to avoid overwriting other language translations on update.
375
+ */
376
+ rawTranslations(enabled?: boolean): this;
338
377
  /**
339
378
  * Execute query and list matching records with pagination metadata
340
379
  */
341
380
  list(): Promise<EntityListResponse<T>>;
342
381
  /**
343
- * Fetch a single entity by ID with optional expansion and flattening
382
+ * Fetch a single entity by ID with optional expansion, translation locale, and flattening
344
383
  */
345
- get(id: string, options?: {
346
- flat?: boolean;
347
- include?: string[];
348
- }): Promise<EntityRecord<T>>;
384
+ get(id: string, options?: GetEntityOptions): Promise<EntityRecord<T>>;
349
385
  /**
350
386
  * Create a new entity record matching schema
351
387
  */
@@ -434,7 +470,16 @@ declare class Zone4CodeClient {
434
470
  readonly schema: SchemaClient;
435
471
  private storage;
436
472
  private fetchFn;
473
+ private currentLanguage;
437
474
  constructor(config: Zone4CodeConfig);
475
+ /**
476
+ * Set the active default language for API requests (e.g., 'en', 'fr', 'ar', or '*' for raw)
477
+ */
478
+ setLanguage(lang: string | null): this;
479
+ /**
480
+ * Get the active default language
481
+ */
482
+ getLanguage(): string | null;
438
483
  /**
439
484
  * Access an entity collection with chainable query builder (like Supabase .from('orders'))
440
485
  */
@@ -472,4 +517,4 @@ declare class MemoryStorage implements StorageAdapter {
472
517
  }
473
518
  declare function getDefaultStorage(): StorageAdapter;
474
519
 
475
- export { AuthClient, type AuthResponse, type AuthUser, type EntityListResponse, EntityQueryBuilder, type EntityRecord, type EntityRelationPayload, type EntityRelationRecord, type EntityReplaceRelationsPayload, type EntityRevision, type EntitySchemaDefinition, type FilterOperator, MemoryStorage, SchemaClient, type SchemaProperty, type StorageAdapter, type UserProfileWithWallet, Zone4CodeClient, type Zone4CodeConfig, createClient, createClient as default, getDefaultStorage };
520
+ export { AuthClient, type AuthResponse, type AuthUser, type EntityListResponse, EntityQueryBuilder, type EntityRecord, type EntityRelationPayload, type EntityRelationRecord, type EntityReplaceRelationsPayload, type EntityRevision, type EntitySchemaDefinition, type FilterOperator, type GetEntityOptions, MemoryStorage, SchemaClient, type SchemaProperty, type StorageAdapter, type Translatable, type UserProfileWithWallet, Zone4CodeClient, type Zone4CodeConfig, createClient, createClient as default, getDefaultStorage };
package/dist/index.d.ts CHANGED
@@ -42,7 +42,17 @@ interface Zone4CodeConfig {
42
42
  * Callback invoked when a user session expires (e.g. refresh token expired or invalid).
43
43
  */
44
44
  onSessionExpired?: () => void;
45
+ /**
46
+ * Default language locale for API requests (e.g. 'en', 'fr', 'ar').
47
+ * Can be overridden per query with `.lang(locale)` or set to '*' for raw translations.
48
+ */
49
+ defaultLanguage?: string;
45
50
  }
51
+ /**
52
+ * Represents either a plain localized string or a dictionary of multilingual values:
53
+ * e.g. "Beach Villa" or { en: "Beach Villa", fr: "Villa Plage", ar: "فيلا الشاطئ" }
54
+ */
55
+ type Translatable<T = string> = T | Record<string, T>;
46
56
  interface AuthUser {
47
57
  id: string;
48
58
  email?: string;
@@ -77,6 +87,12 @@ interface EntityListResponse<T = Record<string, any>> {
77
87
  limit?: number;
78
88
  cursor?: string;
79
89
  }
90
+ interface GetEntityOptions {
91
+ flat?: boolean;
92
+ include?: string[];
93
+ lang?: string;
94
+ rawTranslations?: boolean;
95
+ }
80
96
  interface SchemaProperty {
81
97
  type: 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array';
82
98
  description?: string;
@@ -85,6 +101,11 @@ interface SchemaProperty {
85
101
  items?: SchemaProperty;
86
102
  properties?: Record<string, SchemaProperty>;
87
103
  required?: string[];
104
+ /**
105
+ * If true, this field accepts multilingual JSONB dictionaries ({ en: "...", fr: "..." })
106
+ * and automatically flattens to the active language during queries.
107
+ */
108
+ translatable?: boolean;
88
109
  }
89
110
  interface EntitySchemaDefinition {
90
111
  typeName: string;
@@ -159,6 +180,7 @@ declare class AuthClient {
159
180
  private sessionExpiredListeners;
160
181
  private activeRefreshPromise;
161
182
  private storageLoadedPromise;
183
+ private getLanguage?;
162
184
  private static TOKEN_KEY_PREFIX;
163
185
  constructor(options: {
164
186
  gatewayUrl: string;
@@ -170,6 +192,7 @@ declare class AuthClient {
170
192
  autoRefresh?: boolean;
171
193
  tokenExpiryBuffer?: number;
172
194
  onSessionExpired?: () => void;
195
+ getLanguage?: () => string | null;
173
196
  });
174
197
  getToken(): string | null;
175
198
  getRefreshToken(): string | null;
@@ -287,6 +310,7 @@ declare class EntityQueryBuilder<T = Record<string, any>> {
287
310
  getToken?: () => string | null;
288
311
  fetchFn?: typeof fetch;
289
312
  fetchWithAuth?: (url: string, init?: RequestInit) => Promise<Response>;
313
+ defaultLanguage?: string | null | (() => string | null);
290
314
  });
291
315
  private getHeaders;
292
316
  private executeRequest;
@@ -335,17 +359,29 @@ declare class EntityQueryBuilder<T = Record<string, any>> {
335
359
  offset(offset: number): this;
336
360
  cursor(token: string): this;
337
361
  flat(enabled?: boolean): this;
362
+ /**
363
+ * Set target language for automatic JSONB translation flattening.
364
+ * Example: .lang('fr') returns French fields, falling back to 'en'.
365
+ * Set to '*' or call .rawTranslations() to get raw multilingual dictionaries.
366
+ */
367
+ lang(locale: string): this;
368
+ /**
369
+ * Alias for .lang(locale)
370
+ */
371
+ language(locale: string): this;
372
+ /**
373
+ * Request raw, unflattened multilingual dictionaries ({ en: "...", fr: "..." })
374
+ * Essential for Admin/CMS forms to avoid overwriting other language translations on update.
375
+ */
376
+ rawTranslations(enabled?: boolean): this;
338
377
  /**
339
378
  * Execute query and list matching records with pagination metadata
340
379
  */
341
380
  list(): Promise<EntityListResponse<T>>;
342
381
  /**
343
- * Fetch a single entity by ID with optional expansion and flattening
382
+ * Fetch a single entity by ID with optional expansion, translation locale, and flattening
344
383
  */
345
- get(id: string, options?: {
346
- flat?: boolean;
347
- include?: string[];
348
- }): Promise<EntityRecord<T>>;
384
+ get(id: string, options?: GetEntityOptions): Promise<EntityRecord<T>>;
349
385
  /**
350
386
  * Create a new entity record matching schema
351
387
  */
@@ -434,7 +470,16 @@ declare class Zone4CodeClient {
434
470
  readonly schema: SchemaClient;
435
471
  private storage;
436
472
  private fetchFn;
473
+ private currentLanguage;
437
474
  constructor(config: Zone4CodeConfig);
475
+ /**
476
+ * Set the active default language for API requests (e.g., 'en', 'fr', 'ar', or '*' for raw)
477
+ */
478
+ setLanguage(lang: string | null): this;
479
+ /**
480
+ * Get the active default language
481
+ */
482
+ getLanguage(): string | null;
438
483
  /**
439
484
  * Access an entity collection with chainable query builder (like Supabase .from('orders'))
440
485
  */
@@ -472,4 +517,4 @@ declare class MemoryStorage implements StorageAdapter {
472
517
  }
473
518
  declare function getDefaultStorage(): StorageAdapter;
474
519
 
475
- export { AuthClient, type AuthResponse, type AuthUser, type EntityListResponse, EntityQueryBuilder, type EntityRecord, type EntityRelationPayload, type EntityRelationRecord, type EntityReplaceRelationsPayload, type EntityRevision, type EntitySchemaDefinition, type FilterOperator, MemoryStorage, SchemaClient, type SchemaProperty, type StorageAdapter, type UserProfileWithWallet, Zone4CodeClient, type Zone4CodeConfig, createClient, createClient as default, getDefaultStorage };
520
+ export { AuthClient, type AuthResponse, type AuthUser, type EntityListResponse, EntityQueryBuilder, type EntityRecord, type EntityRelationPayload, type EntityRelationRecord, type EntityReplaceRelationsPayload, type EntityRevision, type EntitySchemaDefinition, type FilterOperator, type GetEntityOptions, MemoryStorage, SchemaClient, type SchemaProperty, type StorageAdapter, type Translatable, type UserProfileWithWallet, Zone4CodeClient, type Zone4CodeConfig, createClient, createClient as default, getDefaultStorage };
package/dist/index.js CHANGED
@@ -48,6 +48,7 @@ var AuthClient = class _AuthClient {
48
48
  sessionExpiredListeners = [];
49
49
  activeRefreshPromise = null;
50
50
  storageLoadedPromise;
51
+ getLanguage;
51
52
  static TOKEN_KEY_PREFIX = "z4c_token_";
52
53
  constructor(options) {
53
54
  const storage = options.storage;
@@ -59,9 +60,10 @@ var AuthClient = class _AuthClient {
59
60
  this.storageKey = storageKey;
60
61
  this.refreshStorageKey = refreshStorageKey;
61
62
  this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
62
- this.autoRefresh = options.autoRefresh ?? true;
63
- this.tokenExpiryBuffer = options.tokenExpiryBuffer ?? 30;
63
+ this.autoRefresh = options.autoRefresh !== void 0 ? options.autoRefresh : true;
64
+ this.tokenExpiryBuffer = options.tokenExpiryBuffer !== void 0 ? options.tokenExpiryBuffer : 30;
64
65
  this.onSessionExpiredCallback = options.onSessionExpired;
66
+ this.getLanguage = options.getLanguage;
65
67
  this.storageLoadedPromise = (async () => {
66
68
  if (options.initialToken) {
67
69
  this.setToken(options.initialToken);
@@ -176,6 +178,10 @@ var AuthClient = class _AuthClient {
176
178
  if (token && !headers["Authorization"] && !headers["authorization"]) {
177
179
  headers["Authorization"] = `Bearer ${token}`;
178
180
  }
181
+ const currentLang = this.getLanguage ? this.getLanguage() : null;
182
+ if (currentLang && !headers["Accept-Language"] && !headers["accept-language"]) {
183
+ headers["Accept-Language"] = currentLang;
184
+ }
179
185
  let res = await this.fetchFn(url, { ...init, headers });
180
186
  if (res.status === 401 && this.autoRefresh && this.getRefreshToken()) {
181
187
  try {
@@ -523,6 +529,12 @@ var EntityQueryBuilder = class {
523
529
  this.getToken = options.getToken || (() => null);
524
530
  this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
525
531
  this.fetchWithAuth = options.fetchWithAuth;
532
+ if (options.defaultLanguage) {
533
+ const langVal = typeof options.defaultLanguage === "function" ? options.defaultLanguage() : options.defaultLanguage;
534
+ if (langVal) {
535
+ this.queryParams.set("lang", langVal);
536
+ }
537
+ }
526
538
  }
527
539
  getHeaders() {
528
540
  const headers = {
@@ -667,6 +679,37 @@ var EntityQueryBuilder = class {
667
679
  this.queryParams.set("flat", enabled ? "true" : "false");
668
680
  return this;
669
681
  }
682
+ /**
683
+ * Set target language for automatic JSONB translation flattening.
684
+ * Example: .lang('fr') returns French fields, falling back to 'en'.
685
+ * Set to '*' or call .rawTranslations() to get raw multilingual dictionaries.
686
+ */
687
+ lang(locale) {
688
+ this.queryParams.set("lang", locale);
689
+ return this;
690
+ }
691
+ /**
692
+ * Alias for .lang(locale)
693
+ */
694
+ language(locale) {
695
+ return this.lang(locale);
696
+ }
697
+ /**
698
+ * Request raw, unflattened multilingual dictionaries ({ en: "...", fr: "..." })
699
+ * Essential for Admin/CMS forms to avoid overwriting other language translations on update.
700
+ */
701
+ rawTranslations(enabled = true) {
702
+ if (enabled) {
703
+ this.queryParams.set("lang", "*");
704
+ this.queryParams.set("rawTranslations", "true");
705
+ } else {
706
+ this.queryParams.delete("rawTranslations");
707
+ if (this.queryParams.get("lang") === "*") {
708
+ this.queryParams.delete("lang");
709
+ }
710
+ }
711
+ return this;
712
+ }
670
713
  // ----------------------------------------------------
671
714
  // Execution Methods
672
715
  // ----------------------------------------------------
@@ -684,13 +727,20 @@ var EntityQueryBuilder = class {
684
727
  return res.json();
685
728
  }
686
729
  /**
687
- * Fetch a single entity by ID with optional expansion and flattening
730
+ * Fetch a single entity by ID with optional expansion, translation locale, and flattening
688
731
  */
689
732
  async get(id, options) {
690
733
  const params = new URLSearchParams(this.queryParams);
691
734
  if (options?.flat !== void 0) {
692
735
  params.set("flat", options.flat ? "true" : "false");
693
736
  }
737
+ if (options?.lang) {
738
+ params.set("lang", options.lang);
739
+ }
740
+ if (options?.rawTranslations) {
741
+ params.set("lang", "*");
742
+ params.set("rawTranslations", "true");
743
+ }
694
744
  if (options?.include && options.include.length > 0) {
695
745
  const existing = params.get("include");
696
746
  const existingList = existing ? existing.split(",").map((s) => s.trim()) : [];
@@ -766,7 +816,8 @@ var EntityQueryBuilder = class {
766
816
  * Fetch all graph relations for this entity record
767
817
  */
768
818
  async getRelations(id, lang) {
769
- const qs = lang ? `?lang=${encodeURIComponent(lang)}` : "";
819
+ const activeLang = lang || this.queryParams.get("lang");
820
+ const qs = activeLang ? `?lang=${encodeURIComponent(activeLang)}` : "";
770
821
  const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations${qs}`;
771
822
  const res = await this.executeRequest(url);
772
823
  if (!res.ok) {
@@ -965,6 +1016,7 @@ var Zone4CodeClient = class {
965
1016
  schema;
966
1017
  storage;
967
1018
  fetchFn;
1019
+ currentLanguage = null;
968
1020
  constructor(config) {
969
1021
  if (!config.gatewayUrl) {
970
1022
  throw new Error("Zone4CodeClient requires a `gatewayUrl` (e.g., http://localhost:8080)");
@@ -976,6 +1028,7 @@ var Zone4CodeClient = class {
976
1028
  this.tenantId = config.tenantId;
977
1029
  this.storage = config.storage || getDefaultStorage();
978
1030
  this.fetchFn = config.fetch || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
1031
+ this.currentLanguage = config.defaultLanguage || null;
979
1032
  this.auth = new AuthClient({
980
1033
  gatewayUrl: this.gatewayUrl,
981
1034
  tenantId: this.tenantId,
@@ -985,7 +1038,8 @@ var Zone4CodeClient = class {
985
1038
  fetchFn: this.fetchFn,
986
1039
  autoRefresh: config.autoRefresh,
987
1040
  tokenExpiryBuffer: config.tokenExpiryBuffer,
988
- onSessionExpired: config.onSessionExpired
1041
+ onSessionExpired: config.onSessionExpired,
1042
+ getLanguage: () => this.getLanguage()
989
1043
  });
990
1044
  this.schema = new SchemaClient({
991
1045
  gatewayUrl: this.gatewayUrl,
@@ -995,6 +1049,19 @@ var Zone4CodeClient = class {
995
1049
  fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
996
1050
  });
997
1051
  }
1052
+ /**
1053
+ * Set the active default language for API requests (e.g., 'en', 'fr', 'ar', or '*' for raw)
1054
+ */
1055
+ setLanguage(lang) {
1056
+ this.currentLanguage = lang;
1057
+ return this;
1058
+ }
1059
+ /**
1060
+ * Get the active default language
1061
+ */
1062
+ getLanguage() {
1063
+ return this.currentLanguage;
1064
+ }
998
1065
  /**
999
1066
  * Access an entity collection with chainable query builder (like Supabase .from('orders'))
1000
1067
  */
@@ -1005,7 +1072,8 @@ var Zone4CodeClient = class {
1005
1072
  typeName,
1006
1073
  getToken: () => this.auth.getToken(),
1007
1074
  fetchFn: this.fetchFn,
1008
- fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
1075
+ fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init),
1076
+ defaultLanguage: () => this.getLanguage()
1009
1077
  });
1010
1078
  }
1011
1079
  /**