@murumets-ee/yhikas-sync 0.40.0 → 0.42.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/dist/index.d.mts +518 -59
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/jobs-CYy8AaKK.mjs +2 -0
- package/dist/jobs-CYy8AaKK.mjs.map +1 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +1 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/sync-state-table-CM6eL5W9.mjs +2 -0
- package/dist/sync-state-table-CM6eL5W9.mjs.map +1 -0
- package/dist/watchdog-dfFi-7re.mjs +2 -0
- package/dist/watchdog-dfFi-7re.mjs.map +1 -0
- package/package.json +7 -7
- package/dist/jobs-D2perJoW.mjs +0 -2
- package/dist/jobs-D2perJoW.mjs.map +0 -1
- package/dist/sync-state-table-CYl_DVX9.mjs +0 -2
- package/dist/sync-state-table-CYl_DVX9.mjs.map +0 -1
- package/dist/watchdog-CNsiJMUR.mjs +0 -2
- package/dist/watchdog-CNsiJMUR.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -30,11 +30,36 @@ declare const YHIKAS_SYNC_ACTOR_ID = "yhikas-sync";
|
|
|
30
30
|
*/
|
|
31
31
|
declare const SYNC_JOB_NAME = "yhikas-sync:pull";
|
|
32
32
|
declare const WATCHDOG_JOB_NAME = "yhikas-sync:staleness-watchdog";
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The three resources in scope.
|
|
35
|
+
*
|
|
36
|
+
* `site_info` was deliberately absent until 2026-08-11 (D027). PR 05 left it
|
|
37
|
+
* out on a MEASUREMENT — `site_info`/`site_notice` appear only in
|
|
38
|
+
* yhikas-admin's `src/db/schema.ts` and its public route, with no admin UI, no
|
|
39
|
+
* server action, no seed and no internal consumer — from which the plan
|
|
40
|
+
* concluded that no rows existed and a sync would pull nothing.
|
|
41
|
+
*
|
|
42
|
+
* Measured against production on 2026-08-11 (R028), that premise had expired:
|
|
43
|
+
* the route answers 200 with populated reception hours and two live notices.
|
|
44
|
+
* The rows were written outside the app. Hardcoding them in the rebuilt site
|
|
45
|
+
* would therefore have frozen live data, not stood in for an empty upstream.
|
|
46
|
+
*/
|
|
34
47
|
declare const ROOM_TYPES_RESOURCE = "room_types";
|
|
35
48
|
declare const LEGAL_DOCUMENTS_RESOURCE = "legal_documents";
|
|
36
|
-
declare const
|
|
49
|
+
declare const SITE_INFO_RESOURCE = "site_info";
|
|
50
|
+
declare const SYNC_RESOURCES: readonly ["room_types", "legal_documents", "site_info"];
|
|
37
51
|
type SyncResource = (typeof SYNC_RESOURCES)[number];
|
|
52
|
+
/**
|
|
53
|
+
* The business key of the one `yhikas_site_info` row.
|
|
54
|
+
*
|
|
55
|
+
* Upstream `site_info` is a singleton and its notices carry no id or code of
|
|
56
|
+
* their own — only `{text, isActive, order}` — so there is no natural key to
|
|
57
|
+
* diff N rows on. Modelling it as ONE row with a constant key keeps the whole
|
|
58
|
+
* package on a single identity story (`code`, `type`, and now `key`), and
|
|
59
|
+
* keeps the notices ordered without inventing an identity that upstream would
|
|
60
|
+
* not preserve across an edit.
|
|
61
|
+
*/
|
|
62
|
+
declare const SITE_INFO_KEY = "default";
|
|
38
63
|
/**
|
|
39
64
|
* Locale codes. Upstream `MultilingualText` is `{ en, et }` and the lumi codes
|
|
40
65
|
* are identical, so there is no mapping to configure — inventing one would be
|
|
@@ -56,6 +81,12 @@ declare const EN_LOCALE = "en";
|
|
|
56
81
|
declare const DECLARED_CURRENCY = "EUR";
|
|
57
82
|
/** `net` = the amount excludes VAT. */
|
|
58
83
|
declare const DECLARED_VAT_TREATMENT = "net";
|
|
84
|
+
/**
|
|
85
|
+
* Hard ceiling on ticker items. A ticker is a marquee: past a couple of dozen
|
|
86
|
+
* entries nobody reads the tail, and an unbounded array from upstream would
|
|
87
|
+
* ride into a JSONB column and out onto every page of the site.
|
|
88
|
+
*/
|
|
89
|
+
declare const MAX_SITE_NOTICES = 25;
|
|
59
90
|
/**
|
|
60
91
|
* Env var carrying the sync's OWN bearer credential — deliberately not the
|
|
61
92
|
* site's `PUBLIC_SITE_API_KEY`. The upstream limiter buckets on
|
|
@@ -144,16 +175,49 @@ declare function planDiff<T>(input: PlanDiffInput<T>): DiffPlan<T>;
|
|
|
144
175
|
* NOT `.strict()`: unknown keys are stripped rather than rejected, so adding a
|
|
145
176
|
* third language upstream degrades to "the sync ignores it" instead of "every
|
|
146
177
|
* run fails".
|
|
178
|
+
*
|
|
179
|
+
* 🔴 **Each locale key is nullable AND optional, and that is the load-bearing
|
|
180
|
+
* clause** (F058, cross-referenced to upstream F031). The column is declared
|
|
181
|
+
* `json(...).notNull()` — and **notNull covers the OBJECT, not its keys**. There
|
|
182
|
+
* is no CHECK constraint, and two of the four public columns
|
|
183
|
+
* (`room_type.name`, `legal_document.title`) have no upstream validation at all:
|
|
184
|
+
* the write is a raw `formData.get(…) as string`, which is `null` the moment a
|
|
185
|
+
* field is absent. So `{"et":"Tuba","en":null}` — and `{"et":"Tuba"}` — are rows
|
|
186
|
+
* yhikas-admin accepts today, through its ordinary admin UI.
|
|
187
|
+
*
|
|
188
|
+
* Requiring both keys would fail the ENTIRE response for one such row, so 32
|
|
189
|
+
* room types would stop syncing because one lacks an English name — every six
|
|
190
|
+
* hours, until somebody edited it upstream. That is exactly the blast-radius
|
|
191
|
+
* mistake `legalDocumentRowSchema.slug` documents below and deliberately
|
|
192
|
+
* avoided; a missing translation belongs at one row, and `projection.ts` is
|
|
193
|
+
* where it is decided (the locale is suppressed, or the row is skipped with a
|
|
194
|
+
* reason).
|
|
195
|
+
*
|
|
196
|
+
* What is NOT relaxed, on purpose:
|
|
197
|
+
*
|
|
198
|
+
* - **The object itself stays required.** A missing translation is DATA; a
|
|
199
|
+
* missing FIELD is a renamed column or a changed envelope. The consequence of
|
|
200
|
+
* tolerating one is caught either way — `assertSomethingSurvived` refuses a
|
|
201
|
+
* snapshot in which no row survived — but refusing here NAMES it
|
|
202
|
+
* (`title: Required`) instead of reporting 32 identical "no 'et' title
|
|
203
|
+
* upstream" skips and leaving the reader to infer the cause.
|
|
204
|
+
* - **A non-string value is still a shape violation.** `{"en":42}` would
|
|
205
|
+
* otherwise be written as a name.
|
|
206
|
+
*
|
|
207
|
+
* Note the asymmetry this leaves, deliberately: a RENAMED key (`et_EE`) is
|
|
208
|
+
* indistinguishable from two absent ones, because unknown keys are stripped by
|
|
209
|
+
* design so a third language degrades gracefully. That case falls through to
|
|
210
|
+
* `assertSomethingSurvived`, which is exactly what it is for.
|
|
147
211
|
*/
|
|
148
212
|
declare const multilingualTextSchema: z.ZodObject<{
|
|
149
|
-
et: z.ZodString
|
|
150
|
-
en: z.ZodString
|
|
213
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
214
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
151
215
|
}, "strip", z.ZodTypeAny, {
|
|
152
|
-
et
|
|
153
|
-
en
|
|
216
|
+
et?: string | null | undefined;
|
|
217
|
+
en?: string | null | undefined;
|
|
154
218
|
}, {
|
|
155
|
-
et
|
|
156
|
-
en
|
|
219
|
+
et?: string | null | undefined;
|
|
220
|
+
en?: string | null | undefined;
|
|
157
221
|
}>;
|
|
158
222
|
type MultilingualText = z.infer<typeof multilingualTextSchema>;
|
|
159
223
|
/**
|
|
@@ -168,14 +232,14 @@ type MultilingualText = z.infer<typeof multilingualTextSchema>;
|
|
|
168
232
|
declare const roomTypeRowSchema: z.ZodObject<{
|
|
169
233
|
code: z.ZodString;
|
|
170
234
|
name: z.ZodObject<{
|
|
171
|
-
et: z.ZodString
|
|
172
|
-
en: z.ZodString
|
|
235
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
236
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
173
237
|
}, "strip", z.ZodTypeAny, {
|
|
174
|
-
et
|
|
175
|
-
en
|
|
238
|
+
et?: string | null | undefined;
|
|
239
|
+
en?: string | null | undefined;
|
|
176
240
|
}, {
|
|
177
|
-
et
|
|
178
|
-
en
|
|
241
|
+
et?: string | null | undefined;
|
|
242
|
+
en?: string | null | undefined;
|
|
179
243
|
}>;
|
|
180
244
|
totalArea: z.ZodNullable<z.ZodString>;
|
|
181
245
|
livingArea: z.ZodNullable<z.ZodString>;
|
|
@@ -188,8 +252,8 @@ declare const roomTypeRowSchema: z.ZodObject<{
|
|
|
188
252
|
}, "strip", z.ZodTypeAny, {
|
|
189
253
|
code: string;
|
|
190
254
|
name: {
|
|
191
|
-
et
|
|
192
|
-
en
|
|
255
|
+
et?: string | null | undefined;
|
|
256
|
+
en?: string | null | undefined;
|
|
193
257
|
};
|
|
194
258
|
totalArea: string | null;
|
|
195
259
|
livingArea: string | null;
|
|
@@ -202,8 +266,8 @@ declare const roomTypeRowSchema: z.ZodObject<{
|
|
|
202
266
|
}, {
|
|
203
267
|
code: string;
|
|
204
268
|
name: {
|
|
205
|
-
et
|
|
206
|
-
en
|
|
269
|
+
et?: string | null | undefined;
|
|
270
|
+
en?: string | null | undefined;
|
|
207
271
|
};
|
|
208
272
|
totalArea: string | null;
|
|
209
273
|
livingArea: string | null;
|
|
@@ -227,14 +291,14 @@ type RoomTypeRow = z.infer<typeof roomTypeRowSchema>;
|
|
|
227
291
|
declare const legalDocumentRowSchema: z.ZodObject<{
|
|
228
292
|
type: z.ZodString;
|
|
229
293
|
title: z.ZodObject<{
|
|
230
|
-
et: z.ZodString
|
|
231
|
-
en: z.ZodString
|
|
294
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
295
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
232
296
|
}, "strip", z.ZodTypeAny, {
|
|
233
|
-
et
|
|
234
|
-
en
|
|
297
|
+
et?: string | null | undefined;
|
|
298
|
+
en?: string | null | undefined;
|
|
235
299
|
}, {
|
|
236
|
-
et
|
|
237
|
-
en
|
|
300
|
+
et?: string | null | undefined;
|
|
301
|
+
en?: string | null | undefined;
|
|
238
302
|
}>;
|
|
239
303
|
/**
|
|
240
304
|
* Accepted as free text HERE, and screened per-row in the projection.
|
|
@@ -255,8 +319,8 @@ declare const legalDocumentRowSchema: z.ZodObject<{
|
|
|
255
319
|
type: string;
|
|
256
320
|
slug: string;
|
|
257
321
|
title: {
|
|
258
|
-
et
|
|
259
|
-
en
|
|
322
|
+
et?: string | null | undefined;
|
|
323
|
+
en?: string | null | undefined;
|
|
260
324
|
};
|
|
261
325
|
order: number;
|
|
262
326
|
htmlContentEt: string;
|
|
@@ -265,8 +329,8 @@ declare const legalDocumentRowSchema: z.ZodObject<{
|
|
|
265
329
|
type: string;
|
|
266
330
|
slug: string;
|
|
267
331
|
title: {
|
|
268
|
-
et
|
|
269
|
-
en
|
|
332
|
+
et?: string | null | undefined;
|
|
333
|
+
en?: string | null | undefined;
|
|
270
334
|
};
|
|
271
335
|
order: number;
|
|
272
336
|
htmlContentEt: string;
|
|
@@ -288,14 +352,14 @@ declare const roomTypesResponseSchema: z.ZodObject<{
|
|
|
288
352
|
roomTypes: z.ZodArray<z.ZodObject<{
|
|
289
353
|
code: z.ZodString;
|
|
290
354
|
name: z.ZodObject<{
|
|
291
|
-
et: z.ZodString
|
|
292
|
-
en: z.ZodString
|
|
355
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
356
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
293
357
|
}, "strip", z.ZodTypeAny, {
|
|
294
|
-
et
|
|
295
|
-
en
|
|
358
|
+
et?: string | null | undefined;
|
|
359
|
+
en?: string | null | undefined;
|
|
296
360
|
}, {
|
|
297
|
-
et
|
|
298
|
-
en
|
|
361
|
+
et?: string | null | undefined;
|
|
362
|
+
en?: string | null | undefined;
|
|
299
363
|
}>;
|
|
300
364
|
totalArea: z.ZodNullable<z.ZodString>;
|
|
301
365
|
livingArea: z.ZodNullable<z.ZodString>;
|
|
@@ -308,8 +372,8 @@ declare const roomTypesResponseSchema: z.ZodObject<{
|
|
|
308
372
|
}, "strip", z.ZodTypeAny, {
|
|
309
373
|
code: string;
|
|
310
374
|
name: {
|
|
311
|
-
et
|
|
312
|
-
en
|
|
375
|
+
et?: string | null | undefined;
|
|
376
|
+
en?: string | null | undefined;
|
|
313
377
|
};
|
|
314
378
|
totalArea: string | null;
|
|
315
379
|
livingArea: string | null;
|
|
@@ -322,8 +386,8 @@ declare const roomTypesResponseSchema: z.ZodObject<{
|
|
|
322
386
|
}, {
|
|
323
387
|
code: string;
|
|
324
388
|
name: {
|
|
325
|
-
et
|
|
326
|
-
en
|
|
389
|
+
et?: string | null | undefined;
|
|
390
|
+
en?: string | null | undefined;
|
|
327
391
|
};
|
|
328
392
|
totalArea: string | null;
|
|
329
393
|
livingArea: string | null;
|
|
@@ -339,8 +403,8 @@ declare const roomTypesResponseSchema: z.ZodObject<{
|
|
|
339
403
|
roomTypes: {
|
|
340
404
|
code: string;
|
|
341
405
|
name: {
|
|
342
|
-
et
|
|
343
|
-
en
|
|
406
|
+
et?: string | null | undefined;
|
|
407
|
+
en?: string | null | undefined;
|
|
344
408
|
};
|
|
345
409
|
totalArea: string | null;
|
|
346
410
|
livingArea: string | null;
|
|
@@ -356,8 +420,8 @@ declare const roomTypesResponseSchema: z.ZodObject<{
|
|
|
356
420
|
roomTypes: {
|
|
357
421
|
code: string;
|
|
358
422
|
name: {
|
|
359
|
-
et
|
|
360
|
-
en
|
|
423
|
+
et?: string | null | undefined;
|
|
424
|
+
en?: string | null | undefined;
|
|
361
425
|
};
|
|
362
426
|
totalArea: string | null;
|
|
363
427
|
livingArea: string | null;
|
|
@@ -374,14 +438,14 @@ declare const legalDocumentsResponseSchema: z.ZodObject<{
|
|
|
374
438
|
documents: z.ZodArray<z.ZodObject<{
|
|
375
439
|
type: z.ZodString;
|
|
376
440
|
title: z.ZodObject<{
|
|
377
|
-
et: z.ZodString
|
|
378
|
-
en: z.ZodString
|
|
441
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
442
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
379
443
|
}, "strip", z.ZodTypeAny, {
|
|
380
|
-
et
|
|
381
|
-
en
|
|
444
|
+
et?: string | null | undefined;
|
|
445
|
+
en?: string | null | undefined;
|
|
382
446
|
}, {
|
|
383
|
-
et
|
|
384
|
-
en
|
|
447
|
+
et?: string | null | undefined;
|
|
448
|
+
en?: string | null | undefined;
|
|
385
449
|
}>;
|
|
386
450
|
/**
|
|
387
451
|
* Accepted as free text HERE, and screened per-row in the projection.
|
|
@@ -402,8 +466,8 @@ declare const legalDocumentsResponseSchema: z.ZodObject<{
|
|
|
402
466
|
type: string;
|
|
403
467
|
slug: string;
|
|
404
468
|
title: {
|
|
405
|
-
et
|
|
406
|
-
en
|
|
469
|
+
et?: string | null | undefined;
|
|
470
|
+
en?: string | null | undefined;
|
|
407
471
|
};
|
|
408
472
|
order: number;
|
|
409
473
|
htmlContentEt: string;
|
|
@@ -412,8 +476,8 @@ declare const legalDocumentsResponseSchema: z.ZodObject<{
|
|
|
412
476
|
type: string;
|
|
413
477
|
slug: string;
|
|
414
478
|
title: {
|
|
415
|
-
et
|
|
416
|
-
en
|
|
479
|
+
et?: string | null | undefined;
|
|
480
|
+
en?: string | null | undefined;
|
|
417
481
|
};
|
|
418
482
|
order: number;
|
|
419
483
|
htmlContentEt: string;
|
|
@@ -425,8 +489,8 @@ declare const legalDocumentsResponseSchema: z.ZodObject<{
|
|
|
425
489
|
type: string;
|
|
426
490
|
slug: string;
|
|
427
491
|
title: {
|
|
428
|
-
et
|
|
429
|
-
en
|
|
492
|
+
et?: string | null | undefined;
|
|
493
|
+
en?: string | null | undefined;
|
|
430
494
|
};
|
|
431
495
|
order: number;
|
|
432
496
|
htmlContentEt: string;
|
|
@@ -438,14 +502,140 @@ declare const legalDocumentsResponseSchema: z.ZodObject<{
|
|
|
438
502
|
type: string;
|
|
439
503
|
slug: string;
|
|
440
504
|
title: {
|
|
441
|
-
et
|
|
442
|
-
en
|
|
505
|
+
et?: string | null | undefined;
|
|
506
|
+
en?: string | null | undefined;
|
|
443
507
|
};
|
|
444
508
|
order: number;
|
|
445
509
|
htmlContentEt: string;
|
|
446
510
|
htmlContentEn: string;
|
|
447
511
|
}[];
|
|
448
512
|
}>;
|
|
513
|
+
/**
|
|
514
|
+
* One `site_notice` — a ticker item.
|
|
515
|
+
*
|
|
516
|
+
* No id, no code, no timestamp: `{text, isActive, order}` is the whole row as
|
|
517
|
+
* the route serves it. That absence is why `site_info` is modelled as one
|
|
518
|
+
* local row carrying an ordered list rather than as N diffable rows — there is
|
|
519
|
+
* no key an edit upstream would preserve.
|
|
520
|
+
*
|
|
521
|
+
* `isActive` is honoured HERE rather than assumed: the route returns inactive
|
|
522
|
+
* notices too, and a ticker that shows a retired notice is worse than one that
|
|
523
|
+
* shows nothing.
|
|
524
|
+
*/
|
|
525
|
+
declare const siteNoticeRowSchema: z.ZodObject<{
|
|
526
|
+
text: z.ZodObject<{
|
|
527
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
528
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
529
|
+
}, "strip", z.ZodTypeAny, {
|
|
530
|
+
et?: string | null | undefined;
|
|
531
|
+
en?: string | null | undefined;
|
|
532
|
+
}, {
|
|
533
|
+
et?: string | null | undefined;
|
|
534
|
+
en?: string | null | undefined;
|
|
535
|
+
}>;
|
|
536
|
+
isActive: z.ZodBoolean;
|
|
537
|
+
order: z.ZodNumber;
|
|
538
|
+
}, "strip", z.ZodTypeAny, {
|
|
539
|
+
text: {
|
|
540
|
+
et?: string | null | undefined;
|
|
541
|
+
en?: string | null | undefined;
|
|
542
|
+
};
|
|
543
|
+
order: number;
|
|
544
|
+
isActive: boolean;
|
|
545
|
+
}, {
|
|
546
|
+
text: {
|
|
547
|
+
et?: string | null | undefined;
|
|
548
|
+
en?: string | null | undefined;
|
|
549
|
+
};
|
|
550
|
+
order: number;
|
|
551
|
+
isActive: boolean;
|
|
552
|
+
}>;
|
|
553
|
+
type SiteNoticeRow = z.infer<typeof siteNoticeRowSchema>;
|
|
554
|
+
/**
|
|
555
|
+
* `/api/public/site-info` — reception hours plus the notice ticker.
|
|
556
|
+
*
|
|
557
|
+
* 🔴 **This envelope cannot express "not configured yet".** The route answers
|
|
558
|
+
* `200 { success: true, receptionHours: {et:'',en:''}, notices: [] }` when no
|
|
559
|
+
* row exists, which is byte-identical to an operator having deliberately
|
|
560
|
+
* cleared everything (F015). The other two resources have no such hole —
|
|
561
|
+
* theirs discriminate on `success` and on array length against a local
|
|
562
|
+
* snapshot.
|
|
563
|
+
*
|
|
564
|
+
* The schema is therefore NOT where that is solved, and deliberately so: the
|
|
565
|
+
* shape is valid either way. `projectSiteInfo` refuses an all-empty payload as
|
|
566
|
+
* unusable (D027), which is the only place that has the standing to decide
|
|
567
|
+
* that a well-formed response is not authoritative.
|
|
568
|
+
*/
|
|
569
|
+
declare const siteInfoResponseSchema: z.ZodObject<{
|
|
570
|
+
success: z.ZodLiteral<true>;
|
|
571
|
+
receptionHours: z.ZodObject<{
|
|
572
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
573
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
574
|
+
}, "strip", z.ZodTypeAny, {
|
|
575
|
+
et?: string | null | undefined;
|
|
576
|
+
en?: string | null | undefined;
|
|
577
|
+
}, {
|
|
578
|
+
et?: string | null | undefined;
|
|
579
|
+
en?: string | null | undefined;
|
|
580
|
+
}>;
|
|
581
|
+
notices: z.ZodArray<z.ZodObject<{
|
|
582
|
+
text: z.ZodObject<{
|
|
583
|
+
et: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
584
|
+
en: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
585
|
+
}, "strip", z.ZodTypeAny, {
|
|
586
|
+
et?: string | null | undefined;
|
|
587
|
+
en?: string | null | undefined;
|
|
588
|
+
}, {
|
|
589
|
+
et?: string | null | undefined;
|
|
590
|
+
en?: string | null | undefined;
|
|
591
|
+
}>;
|
|
592
|
+
isActive: z.ZodBoolean;
|
|
593
|
+
order: z.ZodNumber;
|
|
594
|
+
}, "strip", z.ZodTypeAny, {
|
|
595
|
+
text: {
|
|
596
|
+
et?: string | null | undefined;
|
|
597
|
+
en?: string | null | undefined;
|
|
598
|
+
};
|
|
599
|
+
order: number;
|
|
600
|
+
isActive: boolean;
|
|
601
|
+
}, {
|
|
602
|
+
text: {
|
|
603
|
+
et?: string | null | undefined;
|
|
604
|
+
en?: string | null | undefined;
|
|
605
|
+
};
|
|
606
|
+
order: number;
|
|
607
|
+
isActive: boolean;
|
|
608
|
+
}>, "many">;
|
|
609
|
+
}, "strip", z.ZodTypeAny, {
|
|
610
|
+
receptionHours: {
|
|
611
|
+
et?: string | null | undefined;
|
|
612
|
+
en?: string | null | undefined;
|
|
613
|
+
};
|
|
614
|
+
notices: {
|
|
615
|
+
text: {
|
|
616
|
+
et?: string | null | undefined;
|
|
617
|
+
en?: string | null | undefined;
|
|
618
|
+
};
|
|
619
|
+
order: number;
|
|
620
|
+
isActive: boolean;
|
|
621
|
+
}[];
|
|
622
|
+
success: true;
|
|
623
|
+
}, {
|
|
624
|
+
receptionHours: {
|
|
625
|
+
et?: string | null | undefined;
|
|
626
|
+
en?: string | null | undefined;
|
|
627
|
+
};
|
|
628
|
+
notices: {
|
|
629
|
+
text: {
|
|
630
|
+
et?: string | null | undefined;
|
|
631
|
+
en?: string | null | undefined;
|
|
632
|
+
};
|
|
633
|
+
order: number;
|
|
634
|
+
isActive: boolean;
|
|
635
|
+
}[];
|
|
636
|
+
success: true;
|
|
637
|
+
}>;
|
|
638
|
+
type SiteInfoResponse = z.infer<typeof siteInfoResponseSchema>;
|
|
449
639
|
//#endregion
|
|
450
640
|
//#region src/projection.d.ts
|
|
451
641
|
/** Injected at the seam; see the module docblock for why it is not imported. */
|
|
@@ -479,6 +669,36 @@ interface ProjectionResult {
|
|
|
479
669
|
declare function secondaryLocaleOf(defaultLocale: string): string;
|
|
480
670
|
declare function projectRoomTypes(rows: readonly RoomTypeRow[], options: ProjectionOptions): ProjectionResult;
|
|
481
671
|
declare function projectLegalDocuments(rows: readonly LegalDocumentRow[], options: ProjectionOptions, sanitize: HtmlSanitizer): ProjectionResult;
|
|
672
|
+
/**
|
|
673
|
+
* Reception hours + the notice ticker → the one `yhikas_site_info` row.
|
|
674
|
+
*
|
|
675
|
+
* ## 🔴 The refusal is the point of this function
|
|
676
|
+
*
|
|
677
|
+
* `/api/public/site-info` answers `200 { success: true, receptionHours:
|
|
678
|
+
* {et:'',en:''}, notices: [] }` when no upstream row exists. That is
|
|
679
|
+
* byte-identical to an operator having deliberately cleared both, and it is the
|
|
680
|
+
* state the endpoint is in whenever nobody has filled it in (F015). The other
|
|
681
|
+
* two resources have no equivalent hole — theirs discriminate on `success` and
|
|
682
|
+
* on array length against a local snapshot.
|
|
683
|
+
*
|
|
684
|
+
* The shipped `empty-snapshot` floor cannot cover it, and the reason is
|
|
685
|
+
* structural rather than an oversight: that rule is a row-COUNT test, and this
|
|
686
|
+
* resource always projects exactly one row. The count is 1 whether the row says
|
|
687
|
+
* anything or not, so the floor never fires and an all-empty payload would be
|
|
688
|
+
* applied as an authoritative blanking — emptying the ticker and the hours on
|
|
689
|
+
* every page of the site, silently, six hours after upstream hiccupped.
|
|
690
|
+
*
|
|
691
|
+
* So emptiness is restated here as a CONTENT test: if the default locale has
|
|
692
|
+
* neither hours nor a single active notice, the payload is refused as
|
|
693
|
+
* unusable. Local content is left exactly as it was and the failure arms the
|
|
694
|
+
* staleness watchdog, which is the same posture as an unreachable endpoint —
|
|
695
|
+
* because epistemically it is the same situation.
|
|
696
|
+
*
|
|
697
|
+
* Note what is NOT refused: hours with no notices, or notices with no hours.
|
|
698
|
+
* Both are ordinary states of a real dormitory, and refusing them would make
|
|
699
|
+
* the guard fire on exactly the operator action it exists to protect.
|
|
700
|
+
*/
|
|
701
|
+
declare function projectSiteInfo(response: SiteInfoResponse, options: ProjectionOptions): ProjectionResult;
|
|
482
702
|
//#endregion
|
|
483
703
|
//#region src/apply.d.ts
|
|
484
704
|
/**
|
|
@@ -529,7 +749,7 @@ declare function applyPlan(input: ApplyPlanInput): Promise<ApplyCounts>;
|
|
|
529
749
|
//#region src/entities/guard.d.ts
|
|
530
750
|
/** Thrown when a writer other than the sync tries to mutate synced content. */
|
|
531
751
|
declare class YhikasSyncedContentReadOnlyError extends Error {
|
|
532
|
-
constructor(entityName: string, action: 'create' | 'update' | 'delete' | 'bulk update', actorId: string);
|
|
752
|
+
constructor(entityName: string, action: 'create' | 'update' | 'delete' | 'bulk update' | 'translation write' | 'locale publish change', actorId: string);
|
|
533
753
|
}
|
|
534
754
|
/**
|
|
535
755
|
* Refuse every create/update/delete not performed by the sync actor.
|
|
@@ -539,6 +759,132 @@ declare class YhikasSyncedContentReadOnlyError extends Error {
|
|
|
539
759
|
*/
|
|
540
760
|
declare function machineWritten<N extends string>(entityName: N): Behavior<Record<never, never>, `yhikas-sync:machine-written:${N}`>;
|
|
541
761
|
//#endregion
|
|
762
|
+
//#region src/entities/index.d.ts
|
|
763
|
+
/**
|
|
764
|
+
* The entities this plugin syncs — ONE list, consumed by the plugin's
|
|
765
|
+
* declaration and by the contract tests.
|
|
766
|
+
*
|
|
767
|
+
* Adding `site_info` turned twelve tests red across two files, every one of
|
|
768
|
+
* them because a resource or entity had been re-listed by hand somewhere. This
|
|
769
|
+
* export exists so the next one cannot: `plugin.ts` spreads it into
|
|
770
|
+
* `server.entities`, and `contract.test.ts` iterates it, so a fourth entity is
|
|
771
|
+
* covered by both the moment it is added here.
|
|
772
|
+
*/
|
|
773
|
+
declare const SYNCED_ENTITIES: readonly [import("@murumets-ee/entity").Entity<{
|
|
774
|
+
id: import("@murumets-ee/entity").IdField;
|
|
775
|
+
} & import("@murumets-ee/entity").AuditableFields & import("@murumets-ee/entity").PublishableFields & Record<never, never> & {
|
|
776
|
+
code: import("@murumets-ee/entity").TextField & {
|
|
777
|
+
readonly required: true;
|
|
778
|
+
readonly unique: true;
|
|
779
|
+
readonly indexed: true;
|
|
780
|
+
readonly maxLength: 190;
|
|
781
|
+
};
|
|
782
|
+
name: import("@murumets-ee/entity").TextField & {
|
|
783
|
+
readonly required: true;
|
|
784
|
+
readonly translatable: true;
|
|
785
|
+
};
|
|
786
|
+
totalArea: import("@murumets-ee/entity").TextField & {
|
|
787
|
+
readonly maxLength: 32;
|
|
788
|
+
};
|
|
789
|
+
livingArea: import("@murumets-ee/entity").TextField & {
|
|
790
|
+
readonly maxLength: 32;
|
|
791
|
+
};
|
|
792
|
+
commonArea: import("@murumets-ee/entity").TextField & {
|
|
793
|
+
readonly maxLength: 32;
|
|
794
|
+
};
|
|
795
|
+
capacity: import("@murumets-ee/entity").NumberField & {
|
|
796
|
+
readonly integer: true;
|
|
797
|
+
};
|
|
798
|
+
placesOccupied: import("@murumets-ee/entity").NumberField & {
|
|
799
|
+
readonly integer: true;
|
|
800
|
+
};
|
|
801
|
+
monthlyRent: import("@murumets-ee/entity").TextField & {
|
|
802
|
+
readonly maxLength: 32;
|
|
803
|
+
};
|
|
804
|
+
discountedMonthlyRent: import("@murumets-ee/entity").TextField & {
|
|
805
|
+
readonly maxLength: 32;
|
|
806
|
+
};
|
|
807
|
+
dailyRent: import("@murumets-ee/entity").TextField & {
|
|
808
|
+
readonly maxLength: 32;
|
|
809
|
+
};
|
|
810
|
+
currency: import("@murumets-ee/entity").TextField & {
|
|
811
|
+
readonly required: true;
|
|
812
|
+
readonly maxLength: 3;
|
|
813
|
+
readonly default: "EUR";
|
|
814
|
+
};
|
|
815
|
+
vatTreatment: import("@murumets-ee/entity").SelectField & {
|
|
816
|
+
options: readonly ["net", "gross"];
|
|
817
|
+
} & {
|
|
818
|
+
readonly options: readonly ["net", "gross"];
|
|
819
|
+
readonly required: true;
|
|
820
|
+
readonly default: "net";
|
|
821
|
+
};
|
|
822
|
+
hasEnglish: import("@murumets-ee/entity").BooleanField & {
|
|
823
|
+
readonly required: true;
|
|
824
|
+
readonly default: false;
|
|
825
|
+
};
|
|
826
|
+
sourceHash: import("@murumets-ee/entity").TextField & {
|
|
827
|
+
readonly maxLength: 64;
|
|
828
|
+
};
|
|
829
|
+
}, "yhikas_room_type", [import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").AuditableFields, string>, import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").PublishableFields, "publishable">, import("@murumets-ee/entity").Behavior<Record<never, never>, "yhikas-sync:machine-written:yhikas_room_type">]>, import("@murumets-ee/entity").Entity<{
|
|
830
|
+
id: import("@murumets-ee/entity").IdField;
|
|
831
|
+
} & import("@murumets-ee/entity").AuditableFields & import("@murumets-ee/entity").PublishableFields & Record<never, never> & {
|
|
832
|
+
type: import("@murumets-ee/entity").TextField & {
|
|
833
|
+
readonly required: true;
|
|
834
|
+
readonly unique: true;
|
|
835
|
+
readonly indexed: true;
|
|
836
|
+
readonly maxLength: 190;
|
|
837
|
+
};
|
|
838
|
+
title: import("@murumets-ee/entity").TextField & {
|
|
839
|
+
readonly required: true;
|
|
840
|
+
readonly translatable: true;
|
|
841
|
+
};
|
|
842
|
+
sourceSlug: import("@murumets-ee/entity").TextField & {
|
|
843
|
+
readonly required: true;
|
|
844
|
+
readonly indexed: true;
|
|
845
|
+
readonly maxLength: 190;
|
|
846
|
+
};
|
|
847
|
+
body: import("@murumets-ee/entity").RichTextField & {
|
|
848
|
+
readonly required: true;
|
|
849
|
+
readonly translatable: true;
|
|
850
|
+
};
|
|
851
|
+
order: import("@murumets-ee/entity").NumberField & {
|
|
852
|
+
readonly integer: true;
|
|
853
|
+
readonly required: true;
|
|
854
|
+
readonly default: 0;
|
|
855
|
+
};
|
|
856
|
+
hasEnglish: import("@murumets-ee/entity").BooleanField & {
|
|
857
|
+
readonly required: true;
|
|
858
|
+
readonly default: false;
|
|
859
|
+
};
|
|
860
|
+
sourceHash: import("@murumets-ee/entity").TextField & {
|
|
861
|
+
readonly maxLength: 64;
|
|
862
|
+
};
|
|
863
|
+
}, "yhikas_legal_document", [import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").AuditableFields, string>, import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").PublishableFields, "publishable">, import("@murumets-ee/entity").Behavior<Record<never, never>, "yhikas-sync:machine-written:yhikas_legal_document">]>, import("@murumets-ee/entity").Entity<{
|
|
864
|
+
id: import("@murumets-ee/entity").IdField;
|
|
865
|
+
} & import("@murumets-ee/entity").AuditableFields & import("@murumets-ee/entity").PublishableFields & Record<never, never> & {
|
|
866
|
+
key: import("@murumets-ee/entity").TextField & {
|
|
867
|
+
readonly required: true;
|
|
868
|
+
readonly unique: true;
|
|
869
|
+
readonly indexed: true;
|
|
870
|
+
readonly maxLength: 32;
|
|
871
|
+
};
|
|
872
|
+
receptionHours: import("@murumets-ee/entity").TextField & {
|
|
873
|
+
readonly translatable: true;
|
|
874
|
+
readonly maxLength: 500;
|
|
875
|
+
};
|
|
876
|
+
notices: import("@murumets-ee/entity").JsonField & {
|
|
877
|
+
readonly translatable: true;
|
|
878
|
+
};
|
|
879
|
+
hasEnglish: import("@murumets-ee/entity").BooleanField & {
|
|
880
|
+
readonly default: false;
|
|
881
|
+
readonly required: true;
|
|
882
|
+
};
|
|
883
|
+
sourceHash: import("@murumets-ee/entity").TextField & {
|
|
884
|
+
readonly maxLength: 64;
|
|
885
|
+
};
|
|
886
|
+
}, "yhikas_site_info", [import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").AuditableFields, string>, import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").PublishableFields, "publishable">, import("@murumets-ee/entity").Behavior<Record<never, never>, "yhikas-sync:machine-written:yhikas_site_info">]>];
|
|
887
|
+
//#endregion
|
|
542
888
|
//#region src/entities/legal-document.d.ts
|
|
543
889
|
/**
|
|
544
890
|
* `yhikas_legal_document` — the local projection of yhikas-admin's
|
|
@@ -755,6 +1101,96 @@ declare const YhikasRoomType: import("@murumets-ee/entity").Entity<{
|
|
|
755
1101
|
};
|
|
756
1102
|
}, "yhikas_room_type", [import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").AuditableFields, string>, import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").PublishableFields, "publishable">, import("@murumets-ee/entity").Behavior<Record<never, never>, "yhikas-sync:machine-written:yhikas_room_type">]>;
|
|
757
1103
|
//#endregion
|
|
1104
|
+
//#region src/entities/site-info.d.ts
|
|
1105
|
+
/**
|
|
1106
|
+
* `yhikas_site_info` — reception hours and the notice ticker, as ONE row.
|
|
1107
|
+
*
|
|
1108
|
+
* ## Why a singleton and not N notice rows
|
|
1109
|
+
*
|
|
1110
|
+
* Upstream `site_notice` carries `{text, isActive, order}` and nothing else —
|
|
1111
|
+
* no id, no code, no timestamp. So there is no business key an edit upstream
|
|
1112
|
+
* would preserve, and a diff keyed on `order` would read "notice 2 was
|
|
1113
|
+
* reworded" and "notices 2 and 3 swapped places" as the same event. Modelling
|
|
1114
|
+
* the resource as one row carrying an ordered list keeps identity honest: the
|
|
1115
|
+
* whole list is the value, and it is replaced as a value.
|
|
1116
|
+
*
|
|
1117
|
+
* That also matches what the site does with it. A ticker renders the list in
|
|
1118
|
+
* order; nothing links to an individual notice, so nothing needs one to have a
|
|
1119
|
+
* URL, a version or a publish state of its own.
|
|
1120
|
+
*
|
|
1121
|
+
* ## Both halves are optional, and that is the point
|
|
1122
|
+
*
|
|
1123
|
+
* `receptionHours` and `notices` can each legitimately be empty — a dormitory
|
|
1124
|
+
* with no current notices is an ordinary state. What is NOT ordinary is BOTH
|
|
1125
|
+
* being empty, because that is exactly what `/api/public/site-info` returns
|
|
1126
|
+
* when no row exists at all (F015), and it is indistinguishable at the wire
|
|
1127
|
+
* from a deliberate clearing. `projectSiteInfo` refuses that payload rather
|
|
1128
|
+
* than writing it (D027) — the refusal lives there because emptiness is a
|
|
1129
|
+
* content judgement, not a schema one.
|
|
1130
|
+
*
|
|
1131
|
+
* ## Security
|
|
1132
|
+
*
|
|
1133
|
+
* As with the other two synced entities: `access` is advisory metadata, the
|
|
1134
|
+
* firewall does not read it, and `admin` bypasses it unconditionally. The real
|
|
1135
|
+
* controls are deny-by-default (no non-admin role is granted anything here) and
|
|
1136
|
+
* `machineWritten`, which since PR 12 refuses non-sync writers on the base row
|
|
1137
|
+
* AND on the translation path.
|
|
1138
|
+
*/
|
|
1139
|
+
declare const YHIKAS_SITE_INFO_ENTITY = "yhikas_site_info";
|
|
1140
|
+
declare const YhikasSiteInfo: import("@murumets-ee/entity").Entity<{
|
|
1141
|
+
id: import("@murumets-ee/entity").IdField;
|
|
1142
|
+
} & import("@murumets-ee/entity").AuditableFields & import("@murumets-ee/entity").PublishableFields & Record<never, never> & {
|
|
1143
|
+
/**
|
|
1144
|
+
* The business key, constant at {@link SITE_INFO_KEY}.
|
|
1145
|
+
*
|
|
1146
|
+
* A singleton still needs a key, for the same reason the other two do: the
|
|
1147
|
+
* differ matches local to upstream by key, and `applyPlan` upserts on it.
|
|
1148
|
+
* A constant key makes the singleton a one-element case of the same
|
|
1149
|
+
* machinery rather than a second code path.
|
|
1150
|
+
*/
|
|
1151
|
+
key: import("@murumets-ee/entity").TextField & {
|
|
1152
|
+
readonly required: true;
|
|
1153
|
+
readonly unique: true;
|
|
1154
|
+
readonly indexed: true;
|
|
1155
|
+
readonly maxLength: 32;
|
|
1156
|
+
};
|
|
1157
|
+
/**
|
|
1158
|
+
* Reception hours as free text — `E–R 9–17`, or whatever the office writes.
|
|
1159
|
+
*
|
|
1160
|
+
* Deliberately NOT structured. Upstream stores one multilingual string and
|
|
1161
|
+
* the site renders it verbatim; parsing it into open/close pairs here would
|
|
1162
|
+
* invent a schema neither system has, and would fail the first time someone
|
|
1163
|
+
* writes "suletud 24.06".
|
|
1164
|
+
*/
|
|
1165
|
+
receptionHours: import("@murumets-ee/entity").TextField & {
|
|
1166
|
+
readonly translatable: true;
|
|
1167
|
+
readonly maxLength: 500;
|
|
1168
|
+
};
|
|
1169
|
+
/**
|
|
1170
|
+
* The ticker, as an ordered array of strings for ONE locale.
|
|
1171
|
+
*
|
|
1172
|
+
* Already filtered to `isActive` and already sorted by `order` at
|
|
1173
|
+
* projection time, so a renderer maps over it and nothing more. `isActive`
|
|
1174
|
+
* and `order` are upstream's editing affordances, not content — carrying
|
|
1175
|
+
* them across would oblige every consumer to re-implement the same filter
|
|
1176
|
+
* and sort, and one of them would eventually get it wrong.
|
|
1177
|
+
*/
|
|
1178
|
+
notices: import("@murumets-ee/entity").JsonField & {
|
|
1179
|
+
readonly translatable: true;
|
|
1180
|
+
};
|
|
1181
|
+
/**
|
|
1182
|
+
* Whether the secondary locale has anything publishable. A convenience
|
|
1183
|
+
* flag for the admin list, never the control — per-locale publish status is.
|
|
1184
|
+
*/
|
|
1185
|
+
hasEnglish: import("@murumets-ee/entity").BooleanField & {
|
|
1186
|
+
readonly default: false;
|
|
1187
|
+
readonly required: true;
|
|
1188
|
+
}; /** The change signal. Written LAST, so a stale hash means "redo this row". */
|
|
1189
|
+
sourceHash: import("@murumets-ee/entity").TextField & {
|
|
1190
|
+
readonly maxLength: 64;
|
|
1191
|
+
};
|
|
1192
|
+
}, "yhikas_site_info", [import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").AuditableFields, string>, import("@murumets-ee/entity").Behavior<import("@murumets-ee/entity").PublishableFields, "publishable">, import("@murumets-ee/entity").Behavior<Record<never, never>, "yhikas-sync:machine-written:yhikas_site_info">]>;
|
|
1193
|
+
//#endregion
|
|
758
1194
|
//#region src/entity-client.d.ts
|
|
759
1195
|
/** The `AdminClient` methods the adapter forwards, structurally. */
|
|
760
1196
|
type AdminClientLike = ReturnType<ToolkitApp['getClient']>;
|
|
@@ -930,6 +1366,15 @@ declare class YhikasUpstreamClient {
|
|
|
930
1366
|
fetchRoomTypes(): Promise<RoomTypeRow[]>;
|
|
931
1367
|
/** `GET /api/public/legal-documents`. Active only, ordered by `order`; unpaginated. */
|
|
932
1368
|
fetchLegalDocuments(): Promise<LegalDocumentRow[]>;
|
|
1369
|
+
/**
|
|
1370
|
+
* `GET /api/public/site-info`. Reception hours + notice ticker; a singleton,
|
|
1371
|
+
* so there is nothing to order or paginate.
|
|
1372
|
+
*
|
|
1373
|
+
* Returns the whole envelope rather than one key, because both halves are the
|
|
1374
|
+
* payload and neither is meaningful without the other — a caller deciding
|
|
1375
|
+
* whether this response is usable at all has to see both (D027).
|
|
1376
|
+
*/
|
|
1377
|
+
fetchSiteInfo(): Promise<SiteInfoResponse>;
|
|
933
1378
|
}
|
|
934
1379
|
//#endregion
|
|
935
1380
|
//#region src/run-sync.d.ts
|
|
@@ -937,6 +1382,7 @@ interface SyncRunDeps {
|
|
|
937
1382
|
readonly upstream: YhikasUpstreamClient;
|
|
938
1383
|
readonly roomTypeClient: SyncEntityClient;
|
|
939
1384
|
readonly legalDocumentClient: SyncEntityClient;
|
|
1385
|
+
readonly siteInfoClient: SyncEntityClient;
|
|
940
1386
|
readonly state: SyncStateStore;
|
|
941
1387
|
readonly sanitizeHtml: HtmlSanitizer;
|
|
942
1388
|
readonly logger: SyncLogger;
|
|
@@ -982,7 +1428,20 @@ declare class YhikasUpstreamError extends Error {
|
|
|
982
1428
|
*/
|
|
983
1429
|
declare class YhikasSyncRefusedError extends Error {
|
|
984
1430
|
readonly resource: SyncResource;
|
|
985
|
-
readonly reason: 'empty-snapshot' | 'retire-fraction' | 'oversized-snapshot' | 'duplicate-key'
|
|
1431
|
+
readonly reason: 'empty-snapshot' | 'retire-fraction' | 'oversized-snapshot' | 'duplicate-key'
|
|
1432
|
+
/**
|
|
1433
|
+
* A well-formed 200 whose CONTENT is entirely empty (D027).
|
|
1434
|
+
*
|
|
1435
|
+
* Distinct from `empty-snapshot`, which is a row-COUNT test and therefore
|
|
1436
|
+
* cannot see this: `site_info` always projects exactly one row, so the
|
|
1437
|
+
* count is 1 whether or not that row says anything. `/api/public/site-info`
|
|
1438
|
+
* answers 200 with empty strings and no notices when no upstream row
|
|
1439
|
+
* exists, which is byte-identical to a deliberate clearing (F015) — so the
|
|
1440
|
+
* only safe reading of an all-empty payload is "not authoritative", and the
|
|
1441
|
+
* only safe response is to leave local content alone and let the watchdog
|
|
1442
|
+
* arm.
|
|
1443
|
+
*/
|
|
1444
|
+
| 'empty-content';
|
|
986
1445
|
constructor(resource: SyncResource, reason: YhikasSyncRefusedError['reason'], message: string);
|
|
987
1446
|
}
|
|
988
1447
|
//#endregion
|
|
@@ -1022,5 +1481,5 @@ declare function assessStaleness(records: readonly SyncStateRecord[], now: Date,
|
|
|
1022
1481
|
*/
|
|
1023
1482
|
declare function assertNotStale(records: readonly SyncStateRecord[], now: Date, staleAfterMs: number): ResourceStaleness[];
|
|
1024
1483
|
//#endregion
|
|
1025
|
-
export { API_KEY_ENV_VAR, type AdminClientLike, type ApplyCounts, type ApplyPlanInput, BASE_URL_ENV_VAR, DECLARED_CURRENCY, DECLARED_VAT_TREATMENT, type DiffPlan, EN_LOCALE, ET_LOCALE, type HtmlSanitizer, LEGAL_DOCUMENTS_RESOURCE, type LegalDocumentRow, type LocalRow, type MultilingualText, type PlanDiffInput, type ProjectedRow, type ProjectionOptions, type ProjectionResult, ROOM_TYPES_RESOURCE, type ResolvedYhikasSyncConfig, type ResourceOutcome, type ResourceStaleness, type RoomTypeRow, SYNC_JOB_NAME, SYNC_RESOURCES, type SanityFloor, type SkippedRow, type SyncEntityClient, type SyncLogger, type SyncResource, type SyncRunDeps, type SyncRunSummary, type SyncStateRecord, type SyncStateStore, type UpstreamFailureKind, VAT_TREATMENTS, WATCHDOG_JOB_NAME, YHIKAS_LEGAL_DOCUMENT_ENTITY, YHIKAS_ROOM_TYPE_ENTITY, YHIKAS_SYNC_ACTOR_ID, YHIKAS_SYNC_PLUGIN_NAME, YhikasApplyPartialError, YhikasLegalDocument, YhikasRoomType, type YhikasSyncConfig, YhikasSyncConfigError, YhikasSyncRefusedError, YhikasSyncRunError, YhikasSyncStaleError, YhikasSyncedContentReadOnlyError, YhikasUpstreamClient, type YhikasUpstreamClientOptions, YhikasUpstreamError, applyPlan, assertNotStale, assessStaleness, createSyncStateStore, legalDocumentRowSchema, legalDocumentsResponseSchema, machineWritten, multilingualTextSchema, planDiff, projectLegalDocuments, projectRoomTypes, readLocalRows, resolveYhikasSyncConfig, roomTypeRowSchema, roomTypesResponseSchema, runYhikasSync, secondaryLocaleOf, stableHash, toSyncEntityClient, truncateError, yhikasSyncStateTable };
|
|
1484
|
+
export { API_KEY_ENV_VAR, type AdminClientLike, type ApplyCounts, type ApplyPlanInput, BASE_URL_ENV_VAR, DECLARED_CURRENCY, DECLARED_VAT_TREATMENT, type DiffPlan, EN_LOCALE, ET_LOCALE, type HtmlSanitizer, LEGAL_DOCUMENTS_RESOURCE, type LegalDocumentRow, type LocalRow, MAX_SITE_NOTICES, type MultilingualText, type PlanDiffInput, type ProjectedRow, type ProjectionOptions, type ProjectionResult, ROOM_TYPES_RESOURCE, type ResolvedYhikasSyncConfig, type ResourceOutcome, type ResourceStaleness, type RoomTypeRow, SITE_INFO_KEY, SITE_INFO_RESOURCE, SYNCED_ENTITIES, SYNC_JOB_NAME, SYNC_RESOURCES, type SanityFloor, type SiteInfoResponse, type SiteNoticeRow, type SkippedRow, type SyncEntityClient, type SyncLogger, type SyncResource, type SyncRunDeps, type SyncRunSummary, type SyncStateRecord, type SyncStateStore, type UpstreamFailureKind, VAT_TREATMENTS, WATCHDOG_JOB_NAME, YHIKAS_LEGAL_DOCUMENT_ENTITY, YHIKAS_ROOM_TYPE_ENTITY, YHIKAS_SITE_INFO_ENTITY, YHIKAS_SYNC_ACTOR_ID, YHIKAS_SYNC_PLUGIN_NAME, YhikasApplyPartialError, YhikasLegalDocument, YhikasRoomType, YhikasSiteInfo, type YhikasSyncConfig, YhikasSyncConfigError, YhikasSyncRefusedError, YhikasSyncRunError, YhikasSyncStaleError, YhikasSyncedContentReadOnlyError, YhikasUpstreamClient, type YhikasUpstreamClientOptions, YhikasUpstreamError, applyPlan, assertNotStale, assessStaleness, createSyncStateStore, legalDocumentRowSchema, legalDocumentsResponseSchema, machineWritten, multilingualTextSchema, planDiff, projectLegalDocuments, projectRoomTypes, projectSiteInfo, readLocalRows, resolveYhikasSyncConfig, roomTypeRowSchema, roomTypesResponseSchema, runYhikasSync, secondaryLocaleOf, siteInfoResponseSchema, siteNoticeRowSchema, stableHash, toSyncEntityClient, truncateError, yhikasSyncStateTable };
|
|
1026
1485
|
//# sourceMappingURL=index.d.mts.map
|