@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.
- package/LICENSE +15 -0
- package/README.md +144 -55
- package/dist/LivePreview.astro +17 -0
- package/dist/edit-mode-middleware.d.ts +9 -0
- package/dist/edit-mode-middleware.d.ts.map +1 -0
- package/dist/edit-mode-middleware.js +5 -0
- package/dist/edit-mode-token.d.ts +34 -0
- package/dist/edit-mode-token.d.ts.map +1 -0
- package/dist/edit-mode.d.ts +16 -0
- package/dist/edit-mode.d.ts.map +1 -0
- package/dist/edit-mode.js +71 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -20
- package/dist/localized-content-index.js +258 -47
- package/dist/secrets-codec.d.ts +54 -0
- package/dist/secrets-codec.d.ts.map +1 -0
- package/dist/vite-plugin-strife-store-Tvau120y.js +143 -0
- package/dist/vite-plugin-strife-store-entry.js +1 -1
- package/dist/vite-plugin-strife-store.d.ts.map +1 -1
- package/package.json +51 -18
- package/dist/vite-plugin-strife-store-HEEze8Mw.js +0 -73
|
@@ -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
|
-
* -
|
|
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,
|
|
106
|
+
function loadDocCached(id, docCache) {
|
|
96
107
|
if (!id) return null;
|
|
97
|
-
var key =
|
|
108
|
+
var key = id;
|
|
98
109
|
if (docCache.has(key)) return docCache.get(key);
|
|
99
|
-
var d = load(id,
|
|
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(
|
|
105
|
-
if (!
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
|
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:
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
549
|
-
|
|
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"}
|