@strifeapp/astro 1.0.29 → 1.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.
@@ -62,14 +62,22 @@
62
62
  * - Shape expansion itself does not localize values, except StrifeUri's first
63
63
  * path segment at dereference time.
64
64
  *
65
+ * Collection mapping
66
+ * - This index must map BOTH 'Contents' AND 'Drafts' collections.
67
+ * - Draft entries are emitted with draft: true so consumers can filter.
68
+ * - When building URLs for draft entries, the origin chain falls back to
69
+ * parent draft documents when the published parent lacks the locale slug.
70
+ *
65
71
  * Determinism & engine constraints
66
72
  * - ES5‑only (no for..of, no arrow functions).
67
73
  * - Deterministic shapes under the same inputs.
68
- * - No performance guards (depth/breadth are unbounded); cycles are still safe.
74
+ * - Relation depth is capped at MAX_RELATION_DEPTH hops; cycles are still safe.
69
75
  * -----------------------------------------------------------------------------
70
76
  */
71
77
 
72
78
 
79
+ var MAX_RELATION_DEPTH = 1;
80
+
73
81
  var RESERVED_ROOT_FIELDS = {
74
82
  url: true,
75
83
  collection: true,
@@ -80,8 +88,11 @@ var RESERVED_ROOT_FIELDS = {
80
88
  locale: true,
81
89
  origin: true,
82
90
  id: true,
91
+ docId: true,
83
92
  displayName: true,
84
- dependencies: true
93
+ dependencies: true,
94
+ deleted: true,
95
+ draft: true
85
96
  };
86
97
 
87
98
  function storeAs(name, value) {
@@ -92,21 +103,20 @@ function indexAs(name, value) {
92
103
  return { $value: value, $name: name, $options: { storage: false } };
93
104
  }
94
105
 
95
- function loadDocCached(id, collection, docCache) {
106
+ function loadDocCached(id, docCache) {
96
107
  if (!id) return null;
97
- var key = collection + '::' + id;
108
+ var key = id;
98
109
  if (docCache.has(key)) return docCache.get(key);
99
- var d = load(id, collection);
110
+ var d = load(id, '@all_docs');
100
111
  if (d) docCache.set(key, d);
101
112
  return d;
102
113
  }
103
114
 
104
- function loadTemplateByCollectionCached(collection, tplCache) {
105
- if (!collection) return null;
106
- var key = 'templates/' + collection;
107
- if (tplCache.has(key)) return tplCache.get(key);
108
- var t = load(key, 'templates');
109
- if (t) tplCache.set(key, t);
115
+ function loadTemplateByCollectionCached(templateId, tplCache) {
116
+ if (!templateId) return null;
117
+ if (tplCache.has(templateId)) return tplCache.get(templateId);
118
+ var t = load(templateId, 'templates');
119
+ if (t) tplCache.set(templateId, t);
110
120
  return t;
111
121
  }
112
122
 
@@ -131,18 +141,144 @@ function loadLabelCached(id, labelCache) {
131
141
  return d;
132
142
  }
133
143
 
134
- function buildUrlCached(doc, docCache) {
144
+ function resolveSlug(slug, locale) {
145
+ if (typeof slug === 'string') return slug; // legacy string format
146
+ if (slug && locale && slug[locale]) return slug[locale]; // locale-keyed object
147
+ return null;
148
+ }
149
+
150
+ // Checks if obj is a locale-keyed object where ALL keys are configured locales.
151
+ // Uses configured locales (not BCP 47 syntax) to avoid false positives on
152
+ // objects like { "id": "abc", "no": false } where keys happen to be 2-letter strings.
153
+ function isLocaleObject(obj, configuredLocales) {
154
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false;
155
+ var keys = Object.keys(obj);
156
+ if (keys.length === 0) return false;
157
+ for (var i = 0; i < keys.length; i++) {
158
+ if (configuredLocales.indexOf(keys[i]) === -1) return false;
159
+ }
160
+ return true;
161
+ }
162
+
163
+ function buildUrlCached(doc, locale, docCache, isDraft) {
135
164
  var visited = {};
136
165
  var slugs = [];
137
166
  var c = doc;
138
167
  do {
139
- if (c.slug) slugs.unshift(c.slug);
168
+ var nodeSlug = resolveSlug(c.slug, locale);
169
+ // Draft URL building: if a published ancestor lacks the locale slug, try its draft
170
+ if (!nodeSlug && isDraft) {
171
+ var nodeCollection = c['@metadata'] ? c['@metadata']['@collection'] : null;
172
+ if (nodeCollection !== 'Drafts') {
173
+ var nodeId = c['@metadata'] ? c['@metadata']['@id'] : null;
174
+ if (nodeId) {
175
+ var draftNode = loadDocCached(nodeId + '/draft', docCache);
176
+ if (draftNode) nodeSlug = resolveSlug(draftNode.slug, locale);
177
+ }
178
+ }
179
+ }
180
+ if (nodeSlug) slugs.unshift(nodeSlug);
140
181
  if (!c.origin || visited[c.origin.id]) break;
141
182
  visited[c.origin.id] = true;
142
- c = loadDocCached(c.origin.id, c.origin.collection, docCache);
183
+ c = loadDocCached(c.origin.id, docCache);
143
184
  } while (c);
144
185
 
145
- return slugs.shift() + slugs.join('/');
186
+ // ENG-62: do NOT return slugs.shift() + slugs.join('/').
187
+ // For an empty array that expression evaluates to the literal string "undefined"
188
+ // (because [].shift() is undefined and undefined + '' coerces to "undefined").
189
+ // For a two-element array it drops the separator (['a','b'] -> "ab" instead of "a/b").
190
+ // Both behaviors are confirmed in api-test/Integration/Eng62LinkResolutionDiagnosticTests.
191
+ //
192
+ // We also can't just slugs.join('/'): the root home's slug is "/" by convention,
193
+ // so ['/', 'page'] would produce "//page". Instead, normalize each slug (strip its
194
+ // own leading/trailing slashes so "/" becomes "" and drops out) and prepend a
195
+ // single leading "/" so URLs always start with "/".
196
+ if (slugs.length === 0) return null;
197
+ var parts = [];
198
+ for (var si = 0; si < slugs.length; si++) {
199
+ var s = slugs[si];
200
+ if (typeof s !== 'string') continue;
201
+ // Strip leading and trailing slashes from this segment. Reduces "/" to ""
202
+ // (skipped) and leaves "foo" untouched.
203
+ var stripped = s;
204
+ while (stripped.length > 0 && stripped.charAt(0) === '/') stripped = stripped.substring(1);
205
+ while (stripped.length > 0 && stripped.charAt(stripped.length - 1) === '/') stripped = stripped.substring(0, stripped.length - 1);
206
+ if (stripped.length > 0) parts.push(stripped);
207
+ }
208
+ return '/' + parts.join('/');
209
+ }
210
+
211
+ function isLinkValue(val) {
212
+ return val && typeof val === 'object' && !Array.isArray(val) &&
213
+ typeof val.href === 'string' && typeof val.target === 'string' &&
214
+ isGuid(val.id);
215
+ }
216
+
217
+ function resolveLinkHref(val, locale, docCache) {
218
+ var doc = loadDocCached(val.id, docCache);
219
+ if (!doc) return val.href;
220
+ // ENG-62: do NOT use `'slug' in doc` here. RavenDB's Jint wraps loaded docs in a
221
+ // BlittableObjectInstance whose [[HasProperty]] returns true for ANY property name,
222
+ // making `'slug' in doc` always-true dead code. Use the @metadata collection check
223
+ // (proven reliable) and a value-presence check instead.
224
+ // See api-test/Integration/Eng62LinkResolutionDiagnosticTests for proof.
225
+ var collection = doc['@metadata'] && doc['@metadata']['@collection'];
226
+ if (collection === 'Files' || collection === 'Folders') return val.href;
227
+ if (doc.slug == null) return val.href;
228
+ var url = buildUrlCached(doc, locale, docCache);
229
+ if (!url || url === 'undefined') return val.href;
230
+ return url;
231
+ }
232
+
233
+ function resolveLink(val, locale, docCache) {
234
+ return {
235
+ id: val.id,
236
+ text: val.text,
237
+ href: resolveLinkHref(val, locale, docCache),
238
+ target: val.target
239
+ };
240
+ }
241
+
242
+ function resolveHtmlLinks(html, locale, docCache) {
243
+ if (html.indexOf('data-id="') === -1) return html;
244
+
245
+ var guidPat = '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
246
+
247
+ // data-id before href
248
+ var p1 = new RegExp(
249
+ '<a\\s([^>]*?)data-id="(' + guidPat + ')"([^>]*?)href="([^"]*)"([^>]*?)>',
250
+ 'g'
251
+ );
252
+ // ENG-62: same Jint quirk as resolveLinkHref — `'slug' in doc` is dead code on
253
+ // a wrapped loaded doc. Use collection check + value check instead.
254
+ var result = html.replace(p1, function(match, before, id, mid, href, after) {
255
+ var doc = loadDocCached(id, docCache);
256
+ if (!doc) return match;
257
+ var coll = doc['@metadata'] && doc['@metadata']['@collection'];
258
+ if (coll === 'Files' || coll === 'Folders') return match;
259
+ if (doc.slug == null) return match;
260
+ var url = buildUrlCached(doc, locale, docCache);
261
+ if (!url || url === 'undefined') return match;
262
+ return '<a ' + before + 'data-id="' + id + '"' + mid + 'href="' + url + '"' + after + '>';
263
+ });
264
+
265
+ // href before data-id
266
+ var p2 = new RegExp(
267
+ '<a\\s([^>]*?)href="([^"]*)"([^>]*?)data-id="(' + guidPat + ')"([^>]*?)>',
268
+ 'g'
269
+ );
270
+ result = result.replace(p2, function(match, before, href, mid, id, after) {
271
+ var doc = loadDocCached(id, docCache);
272
+ if (!doc) return match;
273
+ var coll = doc['@metadata'] && doc['@metadata']['@collection'];
274
+ if (coll === 'Files' || coll === 'Folders') return match;
275
+ if (doc.slug == null) return match;
276
+ var url = buildUrlCached(doc, locale, docCache);
277
+ if (!url || url === 'undefined') return match;
278
+ return '<a ' + before + 'href="' + url + '"' + mid + 'data-id="' + id + '"' + after + '>';
279
+ });
280
+
281
+ return result;
146
282
  }
147
283
 
148
284
  function getTranslator(locale, defaultLocale, fallbackToPrimary) {
@@ -156,10 +292,23 @@ function getTranslator(locale, defaultLocale, fallbackToPrimary) {
156
292
  // }
157
293
  //return obj[defaultLocale];
158
294
  }
295
+ // Return non-objects as-is only for the default locale (handles false→true
296
+ // mismatch where editor.localizable is true but stored value is still a
297
+ // plain scalar). Non-default locales get null — the value was never translated.
298
+ if ((typeof obj === 'string' || typeof obj === 'number') && locale === defaultLocale) return obj;
159
299
  return null;
160
300
  };
161
301
  }
162
302
 
303
+ function isLocalized(obj, configuredLocales) {
304
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false;
305
+ if (!obj.hasOwnProperty) return false;
306
+ for (var i = 0; i < configuredLocales.length; i++) {
307
+ if (obj.hasOwnProperty(configuredLocales[i])) return true;
308
+ }
309
+ return false;
310
+ }
311
+
163
312
  function isGuid(s) {
164
313
  return typeof s === 'string' &&
165
314
  /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(s);
@@ -199,14 +348,19 @@ function isStrifeUri(value) {
199
348
  }
200
349
 
201
350
  function tryGetReferencedValue(strifeUri, populationContext, docCache) {
202
- var document = loadDocCached(strifeUri.docId, '@all_docs', docCache);
351
+ var document = loadDocCached(strifeUri.docId, docCache);
203
352
  if (!document) return null;
204
353
 
205
354
  var val = document;
206
355
  if (strifeUri.segments.length > 0) {
207
- // first segment may be localized
356
+ // first segment may be localized; use isLocalized to distinguish locale-keyed objects
208
357
  var first = strifeUri.segments[0];
209
- val = populationContext.translate(val[first]);
358
+ var rawFirst = val[first];
359
+ if (populationContext.isLocalized(rawFirst)) {
360
+ val = populationContext.translate(rawFirst);
361
+ } else {
362
+ val = rawFirst;
363
+ }
210
364
  for (var i = 1; i < strifeUri.segments.length; i++) {
211
365
  if (val == null) break;
212
366
  var segment = strifeUri.segments[i];
@@ -279,7 +433,7 @@ function isChaptersArray(v) {
279
433
  }
280
434
 
281
435
  function populateByShape(document, out, populationContext, visit,
282
- docCache, tplCache, labelCache) {
436
+ docCache, tplCache, labelCache, hopsLeft) {
283
437
  if (!document || typeof document !== 'object') return;
284
438
 
285
439
  for (var prop in document) {
@@ -300,7 +454,7 @@ function populateByShape(document, out, populationContext, visit,
300
454
  if (chapter && chapter['@strife'] && chapter['@strife'].template) {
301
455
  var child = {};
302
456
  populateByShape(chapter, child, populationContext, visit,
303
- docCache, tplCache, labelCache);
457
+ docCache, tplCache, labelCache, hopsLeft);
304
458
  arr.push(child);
305
459
  }
306
460
  }
@@ -312,17 +466,37 @@ function populateByShape(document, out, populationContext, visit,
312
466
  if (isContentTemplateNode(val)) {
313
467
  var nested = {};
314
468
  populateByShape(val, nested, populationContext, visit,
315
- docCache, tplCache, labelCache);
469
+ docCache, tplCache, labelCache, hopsLeft);
316
470
  out[prop] = nested;
317
471
  continue;
318
472
  }
319
473
 
320
474
  // Relations (GUID-strict)
321
475
  if (isRelationArray(val)) {
322
- var related = projectRelationsIfAny(val, populationContext, visit, docCache, tplCache, labelCache);
476
+ var related = projectRelationsIfAny(val, populationContext, visit, docCache, tplCache, labelCache, hopsLeft);
323
477
  if (related) { out[prop] = related; continue; }
324
478
  }
325
479
 
480
+ // Link resolution
481
+ if (isLinkValue(val)) {
482
+ out[prop] = resolveLink(val, populationContext.locale, docCache);
483
+ continue;
484
+ }
485
+ if (Array.isArray(val) && val.length > 0 && isLinkValue(val[0])) {
486
+ var linkArr = [];
487
+ for (var lki = 0; lki < val.length; lki++) {
488
+ linkArr.push(isLinkValue(val[lki]) ? resolveLink(val[lki], populationContext.locale, docCache) : val[lki]);
489
+ }
490
+ out[prop] = linkArr;
491
+ continue;
492
+ }
493
+
494
+ // HTML link resolution (rich text fields)
495
+ if (typeof val === 'string' && val.indexOf('data-id="') !== -1) {
496
+ out[prop] = resolveHtmlLinks(val, populationContext.locale, docCache);
497
+ continue;
498
+ }
499
+
326
500
  // Default: copy-through (no localization inside nested)
327
501
  out[prop] = val;
328
502
  }
@@ -330,19 +504,20 @@ function populateByShape(document, out, populationContext, visit,
330
504
  if (document['@strife'] && !out['@strife']) out['@strife'] = document['@strife'];
331
505
  }
332
506
 
333
- function projectRelationsIfAny(value, populationContext, visit, docCache, tplCache, labelCache) {
507
+ function projectRelationsIfAny(value, populationContext, visit, docCache, tplCache, labelCache, hopsLeft) {
334
508
  if (!isRelationArray(value)) return null;
509
+ if (hopsLeft <= 0) return [];
335
510
  var refs = [];
336
511
  for (var i = 0; i < value.length; i++) {
337
512
  var v = value[i];
338
513
  var idStr = (typeof v === 'string') ? v : (v && v.id);
339
514
  if (isGuid(idStr)) refs.push(idStr);
340
515
  }
341
- return loadRelatedDocumentsRecurse(refs, populationContext, visit, docCache, tplCache, labelCache);
516
+ return loadRelatedDocumentsRecurse(refs, populationContext, visit, docCache, tplCache, labelCache, hopsLeft);
342
517
  }
343
518
 
344
519
  function populateValuesByEditor(document, template, result, populationContext, visit,
345
- docCache, tplCache, labelCache, isRootLevel) {
520
+ docCache, tplCache, labelCache, isRootLevel, hopsLeft) {
346
521
  if (!template || !template.editors) return;
347
522
  var tEditors = template.editors || [];
348
523
  var i, editor;
@@ -357,13 +532,21 @@ function populateValuesByEditor(document, template, result, populationContext, v
357
532
 
358
533
  // Read value with optional localization
359
534
  var rawVal = document[propertyName];
535
+
536
+ // true→false mismatch: editor is no longer localizable but value is still a locale-object
537
+ if (!editor.localizable && isLocaleObject(rawVal, populationContext.locales)) {
538
+ var defVal = rawVal[populationContext.defaultLocale];
539
+ rawVal = (defVal != null) ? defVal : rawVal[Object.keys(rawVal)[0]];
540
+ if (rawVal == null) rawVal = null;
541
+ }
542
+
360
543
  var docValue = (editor.localizable) ? populationContext.translate(rawVal) : rawVal;
361
544
 
362
545
  // StrifeUri resolution
363
546
  docValue = resolveStrifeUriIfAny(docValue, populationContext, docCache);
364
547
 
365
548
  // Relations (GUID-strict) — detect by value shape
366
- var relatedDocs = projectRelationsIfAny(docValue, populationContext, visit, docCache, tplCache, labelCache);
549
+ var relatedDocs = projectRelationsIfAny(docValue, populationContext, visit, docCache, tplCache, labelCache, hopsLeft);
367
550
  if (relatedDocs) { result[propertyName] = relatedDocs; return; }
368
551
 
369
552
  // Chapters (nested without template loads)
@@ -379,7 +562,7 @@ function populateValuesByEditor(document, template, result, populationContext, v
379
562
  if (chapter && chapter['@strife'] && chapter['@strife'].template) {
380
563
  var child = {};
381
564
  populateByShape(chapter, child, populationContext, visit,
382
- docCache, tplCache, labelCache);
565
+ docCache, tplCache, labelCache, hopsLeft);
383
566
  docResults.push(child);
384
567
  }
385
568
  }
@@ -396,12 +579,32 @@ function populateValuesByEditor(document, template, result, populationContext, v
396
579
  if (docValue && typeof docValue === 'object') {
397
580
  var nested = {};
398
581
  populateByShape(docValue, nested, populationContext, visit,
399
- docCache, tplCache, labelCache);
582
+ docCache, tplCache, labelCache, hopsLeft);
400
583
  result[propertyName] = nested;
401
584
  }
402
585
  return;
403
586
  }
404
587
 
588
+ // Link resolution
589
+ if (isLinkValue(docValue)) {
590
+ result[propertyName] = resolveLink(docValue, populationContext.locale, docCache);
591
+ return;
592
+ }
593
+ if (Array.isArray(docValue) && docValue.length > 0 && isLinkValue(docValue[0])) {
594
+ var linkResults = [];
595
+ for (var lki = 0; lki < docValue.length; lki++) {
596
+ linkResults.push(isLinkValue(docValue[lki]) ? resolveLink(docValue[lki], populationContext.locale, docCache) : docValue[lki]);
597
+ }
598
+ result[propertyName] = linkResults;
599
+ return;
600
+ }
601
+
602
+ // HTML link resolution (rich text fields)
603
+ if (typeof docValue === 'string' && docValue.indexOf('data-id="') !== -1) {
604
+ result[propertyName] = resolveHtmlLinks(docValue, populationContext.locale, docCache);
605
+ return;
606
+ }
607
+
405
608
  // Other fields: plain values (localized at root when editor.localizable)
406
609
  if (docValue !== undefined) {
407
610
  result[propertyName] = docValue;
@@ -416,17 +619,18 @@ function populateValuesByEditor(document, template, result, populationContext, v
416
619
  }
417
620
 
418
621
  function projectDocumentWithTemplate(doc, populationContext, visit,
419
- docCache, tplCache, labelCache) {
622
+ docCache, tplCache, labelCache, locale, hopsLeft) {
420
623
  var collection = doc && doc['@metadata'] ? doc['@metadata']['@collection'] : null;
421
- var tpl = loadTemplateByCollectionCached(collection, tplCache);
624
+ var templateId = 'templates/' + (collection === 'Drafts' ? doc['@metadata']['@base-collection'] : collection);
625
+ var tpl = loadTemplateByCollectionCached(templateId, tplCache);
422
626
  var labelsArr = Array.isArray(doc.labels) ? doc.labels : null;
423
627
 
424
628
  var result = {
425
- id: Id(doc),
426
- url: buildUrlCached(doc, docCache),
629
+ id: doc['@metadata']['@id'],
630
+ url: buildUrlCached(doc, locale, docCache),
427
631
  collection: collection,
428
632
  displayName: doc.displayName,
429
- slug: doc.slug,
633
+ slug: resolveSlug(doc.slug, locale),
430
634
  publishedDate: doc.publishedDate,
431
635
  labels: loadRelatedLabels(labelsArr, labelCache)
432
636
  };
@@ -434,14 +638,14 @@ function projectDocumentWithTemplate(doc, populationContext, visit,
434
638
  // Fill fields per template (use root locale via populationContext.translate)
435
639
  if (tpl && tpl.editors) {
436
640
  populateValuesByEditor(doc, tpl, result, populationContext, visit,
437
- docCache, tplCache, labelCache, false);
641
+ docCache, tplCache, labelCache, false, hopsLeft);
438
642
  }
439
643
 
440
644
  return result;
441
645
  }
442
646
 
443
647
  function loadRelatedDocumentsRecurse(refGuids, populationContext, visit,
444
- docCache, tplCache, labelCache) {
648
+ docCache, tplCache, labelCache, hopsLeft) {
445
649
  if (!refGuids) return null;
446
650
 
447
651
  var results = [];
@@ -455,11 +659,11 @@ function loadRelatedDocumentsRecurse(refGuids, populationContext, visit,
455
659
  if (cached) { results.push(cached); continue; }
456
660
 
457
661
  markVisiting(visit, id);
458
- var doc = loadDocCached(id, '@all_docs', docCache);
662
+ var doc = loadDocCached(id, docCache);
459
663
 
460
664
  if (doc) {
461
665
  var projected = projectDocumentWithTemplate(doc, populationContext, visit,
462
- docCache, tplCache, labelCache);
666
+ docCache, tplCache, labelCache, populationContext.locale, hopsLeft - 1);
463
667
  setCachedNode(visit, id, projected);
464
668
  results.push(projected);
465
669
  }
@@ -472,12 +676,15 @@ function loadRelatedDocumentsRecurse(refGuids, populationContext, visit,
472
676
 
473
677
  function mapDocument(document) {
474
678
  var collection = document['@metadata']['@collection'];
475
- var template = load('templates/' + collection, 'templates');
679
+ var templateId = 'templates/' + (collection === 'Drafts' ? document['@metadata']['@base-collection'] : collection);
680
+ var template = load(templateId, 'templates');
476
681
  if (!template || !template.editors || document.deleted || document.archived) return null;
477
682
 
478
683
  var l10n = loadLocalizationSettings();
479
684
  var locales = (l10n && Array.isArray(l10n.locales)) ? l10n.locales : [l10n && l10n.defaultLocale ? l10n.defaultLocale : 'en'];
480
685
 
686
+ var _isDraft = collection === 'Drafts';
687
+
481
688
  // Shared per-document caches (shared across locales)
482
689
  var _docCache = new Map();
483
690
  var _tplCache = new Map();
@@ -522,22 +729,25 @@ function mapDocument(document) {
522
729
  }
523
730
 
524
731
  var translate = getTranslator(currentLocale, l10n.defaultLocale, l10n.fallbackToPrimary);
732
+ var checkLocalized = function (obj) { return isLocalized(obj, locales); };
525
733
 
526
734
  // Root result (stored fields)
527
735
  var result = {
528
- id: Id(document),
736
+ id: storeAs('id',Id(document)),
737
+ docId: storeAs('docId', document['@metadata']['@id']),
529
738
  locale: storeAs('locale', currentLocale),
530
- displayName: document.displayName,
531
- url: template.disableURL ? null : storeAs('url', buildUrlCached(document, _docCache)),
739
+ displayName: storeAs('displayName',document.displayName),
740
+ url: template.disableURL ? null : storeAs('url', buildUrlCached(document, currentLocale, _docCache, _isDraft)),
532
741
  origin: storeAs('origin', document.origin ? document.origin.id : null ),
533
- collection: storeAs('collection', collection),
534
- publishedDate: storeAs('publishedAt', document.publishedDate),
742
+ collection: storeAs('collection', collection === 'Drafts' ? document['@metadata']['@base-collection'] : collection),
743
+ draft: storeAs('draft', (document['@metadata']['@collection'] === 'Drafts')),
744
+ publishedAt: storeAs('publishedAt', document.publishedDate),
535
745
  createdAt: storeAs('createdAt', document.createdAt),
536
746
  changedAt: storeAs('changedAt', document.changedAt),
537
747
  dependencies: storeAs('dependencies', depsArr),
538
748
  //labels: storeAs('labels', loadRelatedLabels(rootLabelIds, _labelCache)),
539
- labels: storeAs('labels', loadRelatedLabels(rootLabelIds, _labelCache).map(label => label.name)),
540
- deleted: document.deleted
749
+ labels: storeAs('labels', loadRelatedLabels(rootLabelIds, _labelCache).map(function(label) { return label.name; })),
750
+ deleted: indexAs('deleted', document.deleted)
541
751
  };
542
752
 
543
753
  // Visit cache per-locale (keeps shapes deterministic per entry)
@@ -545,8 +755,9 @@ function mapDocument(document) {
545
755
 
546
756
  // Build dynamic fields into a temp container using ROOT TEMPLATE (respect localizable)
547
757
  var rootContainer = {};
548
- populateValuesByEditor(document, template, rootContainer, { translate: translate },
549
- visit, _docCache, _tplCache, _labelCache, true);
758
+ populateValuesByEditor(document, template, rootContainer,
759
+ { translate: translate, isLocalized: checkLocalized, locale: currentLocale, defaultLocale: l10n.defaultLocale, locales: locales },
760
+ visit, _docCache, _tplCache, _labelCache, true, MAX_RELATION_DEPTH);
550
761
 
551
762
  // Copy fields from temp container into result
552
763
  // - arrays/objects -> storeAs
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Codec for the single `STRIFE_SECRET` env var.
3
+ *
4
+ * Wire format (dot-separated, JWT-style): `v1.<meta>.<cert>`
5
+ * - `meta` = base64url(JSON.stringify({ urls, database, password?, type?, teamId?, previewSecret? }))
6
+ * - `cert` = base64url(<raw PFX bytes>) — encoded ONCE (no double-base64)
7
+ *
8
+ * This module is the single source of truth for the format. The generation
9
+ * script (encode) and the generated `strife:store` module (decode) both bind to
10
+ * it. It is intentionally dependency-free (Node `Buffer` only) so the decode
11
+ * logic runs on every Node-based runtime, including edge runtimes without
12
+ * `node:zlib`. No compression, no encryption — base64url is encoding, not secrecy.
13
+ *
14
+ * Error messages carry only STRUCTURAL diagnostics — never the blob value, the
15
+ * decoded meta, the certificate bytes, or the password (R7 no-leak): a decode
16
+ * error can surface in an SSR log or error overlay.
17
+ */
18
+ export interface PackedSecrets {
19
+ urls: string[];
20
+ database: string;
21
+ /** Raw PFX bytes (NOT base64). RavenDB receives this Buffer directly. */
22
+ certificate: Buffer;
23
+ password?: string;
24
+ type?: string;
25
+ /**
26
+ * Team/workspace id. Matched against the `workspace` claim of a preview
27
+ * (edit-mode) token. Not secret on its own — an identifier, like an account
28
+ * number.
29
+ */
30
+ teamId?: string;
31
+ /**
32
+ * HMAC secret used to verify preview (edit-mode) tokens. Distinct in purpose
33
+ * from the DB certificate/password, but rides in the same blob so consumers
34
+ * configure a single var. Consumed by edit-mode.ts.
35
+ */
36
+ previewSecret?: string;
37
+ }
38
+ /** Thrown on a malformed blob. Messages are structural-only (no secret content). */
39
+ export declare class SecretsDecodeError extends Error {
40
+ constructor(message: string);
41
+ }
42
+ export declare function encodeSecrets(secrets: PackedSecrets): string;
43
+ /**
44
+ * Decode a `STRIFE_SECRET` blob.
45
+ *
46
+ * @returns the packed secrets, or `null` when the blob carries a *known-shape*
47
+ * but unrecognised version (e.g. a future `v2`). `null` signals the caller to
48
+ * treat `STRIFE_SECRET` as unset and degrade to the four-var fallback chain,
49
+ * rather than aborting module init.
50
+ * @throws {SecretsDecodeError} when the blob is malformed (wrong section count,
51
+ * no version prefix, bad base64url, invalid/incomplete JSON).
52
+ */
53
+ export declare function decodeSecrets(value: string): PackedSecrets | null;
54
+ //# sourceMappingURL=secrets-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets-codec.d.ts","sourceRoot":"","sources":["../src/secrets-codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,oFAAoF;AACpF,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAc5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA0DjE"}