@qikdev/sdk 0.1.211 → 0.1.213

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qikdev/sdk",
3
- "version": "0.1.211",
3
+ "version": "0.1.213",
4
4
  "type": "module",
5
5
  "description": "Promise based Javascript SDK",
6
6
  "main": "./dist/main.js",
@@ -64,8 +64,141 @@ export default function (qik) {
64
64
  ///////////////////////////////////////////////////
65
65
 
66
66
  const glossaryCache = qik.cache.get("user:glossary");
67
+ const glossaryIndexCache = qik.cache.get("user:glossary:index");
68
+ const definitionCache = qik.cache.get("user:glossary:definitions");
67
69
 
68
70
  let inflightGlossaryRequest;
71
+ let inflightGlossaryIndexRequest;
72
+ let inflightDefinitionRequests = {};
73
+ let indexItemsByKey = {};
74
+ let hydrationQueue = [];
75
+ let isHydrating = false;
76
+
77
+ const CONCURRENT_HYDRATION_REQUESTS = 3;
78
+
79
+ /**
80
+ * Start background hydration of all definitions
81
+ * Uses concurrent workers for faster loading
82
+ * @private
83
+ */
84
+ async function startBackgroundHydration(keys) {
85
+ hydrationQueue = [...keys];
86
+ if (isHydrating) return;
87
+ isHydrating = true;
88
+
89
+ // Worker function that pulls from the shared queue
90
+ async function worker() {
91
+ while (hydrationQueue.length > 0) {
92
+ const key = hydrationQueue.shift();
93
+
94
+ // Skip if already populated or loading
95
+ if (indexItemsByKey[key]?.populated) continue;
96
+ if (inflightDefinitionRequests[key]) continue;
97
+
98
+ try {
99
+ await loadDefinition(key);
100
+ } catch (e) {
101
+ console.warn(`Failed to hydrate ${key}:`, e);
102
+ }
103
+ }
104
+ }
105
+
106
+ // Start concurrent workers
107
+ const workers = [];
108
+ for (let i = 0; i < CONCURRENT_HYDRATION_REQUESTS; i++) {
109
+ workers.push(worker());
110
+ }
111
+ await Promise.all(workers);
112
+
113
+ isHydrating = false;
114
+ }
115
+
116
+ /**
117
+ * Load a single definition from the server
118
+ * @private
119
+ */
120
+ async function loadDefinition(key) {
121
+ inflightDefinitionRequests[key] = qik.api.get(`/glossary/${key}`);
122
+
123
+ try {
124
+ const { data } = await inflightDefinitionRequests[key];
125
+
126
+ // Merge full data into existing index item (mutate in place)
127
+ const indexItem = indexItemsByKey[key];
128
+ if (indexItem) {
129
+ Object.assign(indexItem, data, { populated: true });
130
+ }
131
+
132
+ // Also cache separately for getDefinition()
133
+ const cached = indexItem || { ...data, populated: true };
134
+ definitionCache.set(key, cached);
135
+ qik.dispatch("glossary:definition", { key, data: cached });
136
+ return cached;
137
+ } finally {
138
+ inflightDefinitionRequests[key] = null;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Get a full definition for a specific type.
144
+ * Returns cached if available, otherwise fetches from server.
145
+ * If background hydration is in progress, prioritizes this key.
146
+ * @alias content.getDefinition
147
+ * @param {String} key The type key (e.g., 'article', 'profile')
148
+ * @param {Object} options Additional options
149
+ * @param {Boolean} options.reload Force reload from server
150
+ * @returns {Promise<Object>} The full type definition
151
+ * @example
152
+ * const article = await sdk.content.getDefinition('article');
153
+ * console.log(article.fields);
154
+ */
155
+ service.getDefinition = async function (key, options) {
156
+ options = options || {};
157
+
158
+ // Return cached if available and not forcing reload
159
+ let cached = definitionCache.get(key);
160
+ if (cached && !options.reload && !options.refresh) {
161
+ return cached;
162
+ }
163
+
164
+ // If already loading, wait for it
165
+ if (inflightDefinitionRequests[key]) {
166
+ await inflightDefinitionRequests[key];
167
+ return definitionCache.get(key);
168
+ }
169
+
170
+ // Prioritize: move to front of queue
171
+ const queueIndex = hydrationQueue.indexOf(key);
172
+ if (queueIndex > 0) {
173
+ hydrationQueue.splice(queueIndex, 1);
174
+ hydrationQueue.unshift(key);
175
+ }
176
+
177
+ // Load it now
178
+ return loadDefinition(key);
179
+ };
180
+
181
+ /**
182
+ * Start loading all remaining definitions in the background.
183
+ * Requires glossary({ index: true }) to be called first.
184
+ * Uses concurrent requests for faster loading.
185
+ * @alias content.hydrateInBackground
186
+ * @example
187
+ * await sdk.content.glossary({ index: true });
188
+ * sdk.content.hydrateInBackground();
189
+ */
190
+ service.hydrateInBackground = function () {
191
+ const cached = glossaryIndexCache.get("index");
192
+ if (!cached) {
193
+ console.warn(
194
+ "Call glossary({ index: true }) before hydrateInBackground()",
195
+ );
196
+ return;
197
+ }
198
+
199
+ const keys = cached.map((item) => item.key);
200
+ startBackgroundHydration(keys);
201
+ };
69
202
 
70
203
  /**
71
204
  * Inflate a single value from compressed format
@@ -203,15 +336,62 @@ export default function (qik) {
203
336
  * @param {Boolean} options.reload Force glossary to reload and not be cached.
204
337
  If false will retrieve content type data from the in memory cache
205
338
  * @param {Boolean} options.uncompressed Use the uncompressed endpoint
339
+ * @param {Boolean} options.index Use lightweight index mode with background hydration
206
340
  * @example
207
341
  *
208
342
  * const { article, profile } = await sdk.content.glossary({hash:true});
209
343
  * // Use compressed endpoint for faster loading
210
344
  * const glossary = await sdk.content.glossary({compressed:true});
345
+ * // Use lightweight index with background hydration
346
+ * const glossary = await sdk.content.glossary({index:true, hash:true});
211
347
  */
212
348
  service.glossary = async function (options) {
213
349
  options = options || {};
214
350
 
351
+ // Index mode: lightweight load with background hydration
352
+ if (options.index) {
353
+ let cached = glossaryIndexCache.get("index");
354
+ const reload = options.reload || options.refresh || !cached;
355
+
356
+ if (reload) {
357
+ if (!inflightGlossaryIndexRequest) {
358
+ inflightGlossaryIndexRequest = qik.api.get("/glossary/index");
359
+ inflightGlossaryIndexRequest.then(
360
+ () => {
361
+ inflightGlossaryIndexRequest = null;
362
+ },
363
+ () => {
364
+ inflightGlossaryIndexRequest = null;
365
+ },
366
+ );
367
+ }
368
+
369
+ const { data } = await inflightGlossaryIndexRequest;
370
+
371
+ // Add populated:false to each item and build lookup map
372
+ data.forEach((item) => {
373
+ item.populated = false;
374
+ indexItemsByKey[item.key] = item;
375
+ });
376
+
377
+ glossaryIndexCache.set("index", data);
378
+ cached = data;
379
+ qik.dispatch("glossary:index", data);
380
+ // No auto-hydration - call hydrateInBackground() explicitly if needed
381
+ }
382
+
383
+ if (options.hash) {
384
+ return qik.utils.hash(cached, "key");
385
+ }
386
+
387
+ if (options.hex) {
388
+ return qik.utils.hash(cached, "hex");
389
+ }
390
+
391
+ return cached;
392
+ }
393
+
394
+ // Full glossary mode (existing behavior)
215
395
  // Check if we already have the glossary cached
216
396
  let cached = glossaryCache.get("glossary");
217
397
  const uncompressed = !!options.uncompressed;
package/src/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "0.1.211";
1
+ export const version = "0.1.213";