@murumets-ee/yhikas-sync 0.93.0 → 0.94.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.
Files changed (57) hide show
  1. package/README.md +108 -0
  2. package/dist/client-J_w85GUr.mjs +2 -0
  3. package/dist/client-J_w85GUr.mjs.map +1 -0
  4. package/dist/config-DM3zAAhm.d.mts +244 -0
  5. package/dist/config-DM3zAAhm.d.mts.map +1 -0
  6. package/dist/constants-Bmm3P17G.mjs +2 -0
  7. package/dist/constants-Bmm3P17G.mjs.map +1 -0
  8. package/dist/en-BOWwWf-X.mjs +2 -0
  9. package/dist/en-BOWwWf-X.mjs.map +1 -0
  10. package/dist/et-CGDUOj83.mjs +2 -0
  11. package/dist/et-CGDUOj83.mjs.map +1 -0
  12. package/dist/i18n.d.mts +18 -0
  13. package/dist/i18n.d.mts.map +1 -0
  14. package/dist/i18n.mjs +2 -0
  15. package/dist/i18n.mjs.map +1 -0
  16. package/dist/index.d.mts +244 -774
  17. package/dist/index.d.mts.map +1 -1
  18. package/dist/index.mjs +2 -1
  19. package/dist/index.mjs.map +1 -0
  20. package/dist/jobs-Di4Z8go9.mjs +2 -0
  21. package/dist/jobs-Di4Z8go9.mjs.map +1 -0
  22. package/dist/plugin.d.mts +6 -4
  23. package/dist/plugin.d.mts.map +1 -1
  24. package/dist/plugin.mjs +1 -1
  25. package/dist/plugin.mjs.map +1 -1
  26. package/dist/rolldown-runtime-DK3Fl9T5.mjs +1 -0
  27. package/dist/room-page-registry-BFbyGTC7.mjs +2 -0
  28. package/dist/room-page-registry-BFbyGTC7.mjs.map +1 -0
  29. package/dist/room-page-registry-CnqXH_Ls.d.mts +974 -0
  30. package/dist/room-page-registry-CnqXH_Ls.d.mts.map +1 -0
  31. package/dist/room-pages-adapters-DxFWkFFF.mjs +2 -0
  32. package/dist/room-pages-adapters-DxFWkFFF.mjs.map +1 -0
  33. package/dist/room-pages-table-Vd9BqTSl.mjs +2 -0
  34. package/dist/room-pages-table-Vd9BqTSl.mjs.map +1 -0
  35. package/dist/room-type-DtMaQbza.mjs +2 -0
  36. package/dist/room-type-DtMaQbza.mjs.map +1 -0
  37. package/dist/ru-uzvamDhj.mjs +2 -0
  38. package/dist/ru-uzvamDhj.mjs.map +1 -0
  39. package/dist/sync-state-table-CZjRypK0.mjs +2 -0
  40. package/dist/sync-state-table-CZjRypK0.mjs.map +1 -0
  41. package/dist/widgets-client.d.mts +27 -0
  42. package/dist/widgets-client.d.mts.map +1 -0
  43. package/dist/widgets-client.mjs +3 -0
  44. package/dist/widgets-client.mjs.map +1 -0
  45. package/dist/widgets.d.mts +73 -0
  46. package/dist/widgets.d.mts.map +1 -0
  47. package/dist/widgets.mjs +2 -0
  48. package/dist/widgets.mjs.map +1 -0
  49. package/package.json +41 -7
  50. package/dist/config-ClDwmosW.d.mts +0 -102
  51. package/dist/config-ClDwmosW.d.mts.map +0 -1
  52. package/dist/jobs-Vg-eMVGf.mjs +0 -2
  53. package/dist/jobs-Vg-eMVGf.mjs.map +0 -1
  54. package/dist/sync-state-table-Dq_wyKfo.mjs +0 -2
  55. package/dist/sync-state-table-Dq_wyKfo.mjs.map +0 -1
  56. package/dist/watchdog-C_ClM4PH.mjs +0 -2
  57. package/dist/watchdog-C_ClM4PH.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/constants.ts","../src/diff.ts","../src/upstream/wire.ts","../src/projection.ts","../src/apply.ts","../src/entities/guard.ts","../src/entities/legal-document.ts","../src/entities/room-type.ts","../src/entities/index.ts","../src/entities/site-info.ts","../src/entity-client.ts","../src/sync-state-table.ts","../src/sync-state.ts","../src/upstream/client.ts","../src/run-sync.ts","../src/upstream/errors.ts","../src/watchdog.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/entities/guard.ts","../src/entities/legal-document.ts","../src/entities/room-type.ts","../src/entities/index.ts","../src/entities/site-info.ts","../src/room-pages.ts","../src/sync-state-table.ts","../src/sync-state.ts","../src/room-facts.ts","../src/room-page-record.ts","../src/room-pages-adapters.ts","../src/upstream/client.ts","../src/run-sync.ts","../src/upstream/errors.ts","../src/watchdog.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1 +1,2 @@
1
- import{C as e,D as t,E as n,O as r,S as i,T as a,_ as o,a as s,b as c,c as l,d as u,f as d,h as f,i as p,l as m,m as h,n as g,o as _,p as v,r as y,s as b,t as x,u as S,v as C,w,x as T,y as E}from"./watchdog-C_ClM4PH.mjs";import{A as D,C as O,D as k,E as A,M as j,N as M,P as N,_ as P,a as F,b as I,c as L,d as R,f as z,g as B,h as V,j as H,k as U,l as W,m as G,n as K,o as q,p as J,s as Y,t as X,u as Z,v as Q,w as $,x as ee,y as te}from"./sync-state-table-Dq_wyKfo.mjs";export{B as API_KEY_ENV_VAR,P as BASE_URL_ENV_VAR,Q as DECLARED_CURRENCY,te as DECLARED_VAT_TREATMENT,I as EN_LOCALE,ee as ET_LOCALE,O as LEGAL_DOCUMENTS_RESOURCE,$ as MAX_SITE_NOTICES,A as ROOM_TYPES_RESOURCE,k as SITE_INFO_KEY,U as SITE_INFO_RESOURCE,K as SYNCED_ENTITIES,D as SYNC_JOB_NAME,H as SYNC_RESOURCES,Y as VAT_TREATMENTS,j as WATCHDOG_JOB_NAME,Z as YHIKAS_LEGAL_DOCUMENT_ENTITY,L as YHIKAS_ROOM_TYPE_ENTITY,F as YHIKAS_SITE_INFO_ENTITY,M as YHIKAS_SYNC_ACTOR_ID,N as YHIKAS_SYNC_PLUGIN_NAME,n as YhikasApplyPartialError,R as YhikasLegalDocument,W as YhikasRoomType,q as YhikasSiteInfo,G as YhikasSyncConfigError,w as YhikasSyncRefusedError,h as YhikasSyncRunError,x as YhikasSyncStaleError,z as YhikasSyncedContentReadOnlyError,p as YhikasUpstreamClient,a as YhikasUpstreamError,t as applyPlan,g as assertNotStale,y as assessStaleness,d as createSyncStateStore,s as legalDocumentRowSchema,_ as legalDocumentsResponseSchema,J as machineWritten,b as multilingualTextSchema,i as planDiff,o as projectLegalDocuments,C as projectRoomTypes,E as projectSiteInfo,r as readLocalRows,V as resolveYhikasSyncConfig,l as roomTypeRowSchema,m as roomTypesResponseSchema,f as runYhikasSync,c as secondaryLocaleOf,S as siteInfoResponseSchema,u as siteNoticeRowSchema,e as stableHash,T as toSyncEntityClient,v as truncateError,X as yhikasSyncStateTable};
1
+ import{A as e,C as t,D as n,E as r,M as i,N as a,O as o,S as ee,T as te,_ as s,a as ne,b as c,c as l,d as u,f as re,g as ie,h as ae,i as oe,j as se,k as ce,l as le,m as ue,n as de,o as fe,p as pe,r as me,s as d,t as f,v as p,w as m,y as h}from"./client-J_w85GUr.mjs";import{C as g,S as _,_ as v,b as y,d as b,g as x,i as S,m as C,n as w,p as T,r as E,t as D,u as O,w as k,x as A,y as j}from"./constants-Bmm3P17G.mjs";import{c as M,d as N,f as P,h as F,l as I,m as L,n as R,o as z,p as he,r as ge,s as _e,u as ve}from"./sync-state-table-CZjRypK0.mjs";import{a as ye,n as B,o as be,r as V,t as xe}from"./room-type-DtMaQbza.mjs";import{a as H,n as Se,r as Ce,t as we}from"./room-pages-table-Vd9BqTSl.mjs";import{a as U,n as Te,r as Ee,t as De}from"./room-page-registry-BFbyGTC7.mjs";import{a as Oe,i as ke,o as Ae,r as je,t as Me}from"./room-pages-adapters-DxFWkFFF.mjs";import{getPluginConfig as Ne,hasPluginConfig as Pe}from"@murumets-ee/core";const Fe=[`monthly`,`discounted`,`daily`,`places`,`area`],W={et:`/kuu`,en:`/ month`,ru:`/ мес.`},G=[{name:`monthly`,label:{et:`Kuuüür`,en:`Monthly rent`,ru:`Аренда в месяц`},format:`price`,unit:W},{name:`discounted`,label:`EKA/EMTA`,format:`price`,unit:W},{name:`daily`,label:{et:`päev`,en:`per day`,ru:`в день`},format:`price`},{name:`places`,label:{et:`Kohti`,en:`Places`,ru:`Мест`},format:`number`,unit:{et:`kohta`,en:`places`,ru:`мест`}},{name:`area`,label:{et:`Pind`,en:`Area`,ru:`Площадь`},format:`number`,unit:`m²`}],K=[`liveFacts.values`];function q(e){if(e===null)return;let t=e.trim();if(!/^\d+(\.\d+)?$/.test(t))return;let n=Number(t);return Number.isFinite(n)?n:void 0}function J(e){let t={},n=q(e.monthlyRent),r=q(e.discountedMonthlyRent),i=q(e.dailyRent),a=q(e.totalArea);return n!==void 0&&(t.monthly=n),r!==void 0&&(t.discounted=r),i!==void 0&&(t.daily=i),e.capacity!==null&&e.capacity>=0&&(t.places=e.capacity),a!==void 0&&(t.area=a),t}function Y(e,t){let n=new Map(e.map(e=>[e.code,e])),r={};for(let e of t){let t=n.get(e);if(t===void 0)continue;let i=J(t);Object.keys(i).length>0&&(r[e]={facts:i})}return r}function X(e,t){return e.filter(e=>t({code:e.code,name:e.name})).sort((e,t)=>e.code.localeCompare(t.code)).slice(0,50).map(e=>{let t=e.name.trim()===``?e.code:`${e.code} — ${e.name.trim()}`;return{key:e.code,label:t.slice(0,120)}})}const Ie={"@every 6h":`YhikasSync.provenance.cadenceEvery6Hours`,"@every 1h":`YhikasSync.provenance.cadenceHourly`,"@hourly":`YhikasSync.provenance.cadenceHourly`,"@every 12h":`YhikasSync.provenance.cadenceEvery12Hours`,"@every 24h":`YhikasSync.provenance.cadenceDaily`,"@every 1d":`YhikasSync.provenance.cadenceDaily`,"@daily":`YhikasSync.provenance.cadenceDaily`},Le={s:[`second`,`seconds`],m:[`minute`,`minutes`],h:[`hour`,`hours`],d:[`day`,`days`]},Z={"@hourly":`hour`,"@daily":`day`,"@weekly":`week`,"@monthly":`month`};function Q(e){let t=Ie[e],n=/^@every\s+(\d+)(s|m|h|d)$/.exec(e),r;if(n!==null){let e=Number(n[1]),t=Le[n[2]??``]??[`unit`,`units`];r=e===1?`every ${t[0]}`:`every ${e} ${t[1]}`}else r=Z[e]===void 0?`on a schedule (${e})`.slice(0,120):`every ${Z[e]}`;return t===void 0?{text:r}:{text:r,key:t}}const Re={async rooms(){let{getApp:e}=await import(`@murumets-ee/core`),{toRoomTypeReader:t}=await import(`./room-pages-adapters-DxFWkFFF.mjs`).then(e=>e.n),n=e(),r=n.entities.get(B),i=r===void 0?V:r;return t(n.getClient(i))},async syncState(){let{getApp:e}=await import(`@murumets-ee/core`),{createSyncStateStore:t}=await import(`./client-J_w85GUr.mjs`).then(e=>e.u),{yhikasSyncStateTable:n}=await import(`./sync-state-table-CZjRypK0.mjs`).then(e=>e.t);return t(n.makeClient(e().db.readOnly)).readAll()},syncConfig(){return Pe(`@murumets-ee/yhikas-sync`)?Ne(k):void 0},now:()=>new Date};function $(e,t,n){let r=e.find(e=>e.resource===x),i=c(e,t,n).find(e=>e.resource===x),a=r?.lastSuccessAt==null||i===void 0||i.stale;return{...r?.lastSuccessAt==null?{}:{lastSyncedAt:r.lastSuccessAt.toISOString()},...a?{stale:!0}:{}}}function ze(e={},t=Re){let n=e.adminUrl===void 0?void 0:P(e.adminUrl,`yhikasRoomFacts({ adminUrl })`);return{label:e.label??`Room facts`,labelKey:`YhikasSync.facts.sourceLabel`,kind:`facts`,facts:G,get provenance(){let r=Q(e.schedule??t.syncConfig()?.schedule??`@every 6h`);return{system:`yhikas-admin`,record:`room type`,recordKey:`YhikasSync.provenance.recordRoomType`,cadence:r.text,...r.key===void 0?{}:{cadenceKey:r.key},...n===void 0?{}:{editUrl:`${n}/admin/toad`}}},fields:e.fields??K,async resolve({keys:e,locale:n}){return e.length===0?{}:Y(await(await t.rooms()).published(n,{limit:Math.min(e.length,200),codes:e}),e)},async items({locale:n}){let r=e.offered??t.syncConfig()?.roomPages?.offered??F;return X(await(await t.rooms()).published(n,{limit:200}),r)},async freshness(){let n=e.staleAfterMs??t.syncConfig()?.staleAfterMs??864e5;return $(await t.syncState(),t.now(),n)}}}async function Be(e,t){Ee(e,t);let{getApp:n}=await import(`@murumets-ee/core`);return U(H.makeClient(n().db.readWrite)).record(e,t,`registered`,new Date)}export{D as API_KEY_ENV_VAR,L as AREA_PRICED_UNIT_NAMES,w as BASE_URL_ENV_VAR,E as DECLARED_CURRENCY,S as DECLARED_VAT_TREATMENT,K as DEFAULT_ROOM_FACT_FIELDS,O as EN_LOCALE,b as ET_LOCALE,T as LEGAL_DOCUMENTS_RESOURCE,ue as MAX_ROOM_PAGES_PER_RUN,C as MAX_SITE_NOTICES,ve as PAGE_SLUG_PATTERN,G as ROOM_FACTS,Fe as ROOM_FACT_NAMES,we as ROOM_PAGE_CLAIM_LEASE_MS,Se as ROOM_PAGE_ORIGINS,De as ROOM_PAGE_READ_CHUNK,Ce as ROOM_PAGE_REFUSAL_KINDS,x as ROOM_TYPES_RESOURCE,v as SITE_INFO_KEY,j as SITE_INFO_RESOURCE,ge as SYNCED_ENTITIES,y as SYNC_JOB_NAME,A as SYNC_RESOURCES,xe as VAT_TREATMENTS,_ as WATCHDOG_JOB_NAME,M as YHIKAS_LEGAL_DOCUMENT_ENTITY,B as YHIKAS_ROOM_TYPE_ENTITY,z as YHIKAS_SITE_INFO_ENTITY,g as YHIKAS_SYNC_ACTOR_ID,k as YHIKAS_SYNC_PLUGIN_NAME,se as YhikasApplyPartialError,I as YhikasLegalDocument,Te as YhikasRoomPageRecordError,V as YhikasRoomType,_e as YhikasSiteInfo,N as YhikasSyncConfigError,ce as YhikasSyncRefusedError,re as YhikasSyncRunError,p as YhikasSyncStaleError,ye as YhikasSyncedContentReadOnlyError,f as YhikasUpstreamClient,e as YhikasUpstreamError,i as applyPlan,h as assertNotStale,c as assessStaleness,Q as cadenceOf,ae as classifyRefusal,Me as columnOf,ie as createRoomPageDrafts,U as createRoomPageRegistry,le as createSyncStateStore,q as decimalOf,Oe as isPublishedRoomType,F as isRoomOffer,de as legalDocumentRowSchema,me as legalDocumentsResponseSchema,be as machineWritten,oe as multilingualTextSchema,n as planDiff,ee as projectLegalDocuments,t as projectRoomTypes,m as projectSiteInfo,a as readLocalRows,Be as recordRoomPage,he as resolveYhikasSyncConfig,X as roomFactItemsFrom,Y as roomFactsFrom,J as roomFactsOf,s as roomPageSlug,Ae as roomTypeForPageFrom,ne as roomTypeRowSchema,$ as roomTypesFreshness,fe as roomTypesResponseSchema,pe as runYhikasSync,te as secondaryLocaleOf,d as siteInfoResponseSchema,l as siteNoticeRowSchema,o as stableHash,je as toRoomPageWriter,ke as toRoomTypeReader,r as toSyncEntityClient,u as truncateError,ze as yhikasRoomFacts,H as yhikasRoomPagesTable,R as yhikasSyncStateTable};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/room-facts.ts","../src/room-page-record.ts"],"sourcesContent":["/**\n * `yhikasRoomFacts()` — the synced room types as a LIVE FACTS source\n * (block-library PR21, D010): each room page's price block is a generic live\n * block bound to one room type `code`, showing whichever of its facts the\n * editor ticked — monthly rent, EKA/EMTA rent, daily rent, places, area.\n *\n * @example\n * ```ts\n * // lumi.config.ts\n * import { yhikasRoomFacts } from '@murumets-ee/yhikas-sync'\n * blocksPlugin({ liveSources: { roomFacts: yhikasRoomFacts({ adminUrl: 'https://admin.yhikas.ee' }) } })\n * ```\n *\n * ## Safe to import from `lumi.config.ts`\n *\n * Constructing the source reads nothing and imports nothing server-only:\n * `@murumets-ee/core` is imported lazily INSIDE `resolve` / `items` /\n * `freshness`, which only ever run in a request or on the canvas.\n *\n * ## 🔴 Read as the caller, published rows only\n *\n * Every read goes through `getApp().getClient(...)` — the REQUEST's own user\n * (the visitor on the site, the author on the canvas); never an elevated\n * client. A caller who may not read room types gets a refusal, which the\n * toolkit turns into \"no value\". And only PUBLISHED rows answer: unpublishing\n * is how the sync retires a room, so a retired room draws nothing on the site\n * (and red on the canvas) instead of an old price.\n *\n * ## Freshness\n *\n * The answer is stale when room types have not synced successfully within the\n * sync's staleness window (`staleAfterMs`, 24 h by default) — an old price is\n * worse than none, so the site then draws no prices at all and the canvas\n * draws them red.\n */\n\nimport type {\n LiveFactSpec,\n LiveFreshness,\n LiveItem,\n LiveProvenance,\n LiveSourceSpec,\n} from '@murumets-ee/blocks/schema'\nimport { getPluginConfig, hasPluginConfig } from '@murumets-ee/core'\nimport { resolveAdminUrl } from './config.js'\nimport {\n DEFAULT_STALE_AFTER_MS,\n DEFAULT_SYNC_SCHEDULE,\n ROOM_TYPES_RESOURCE,\n YHIKAS_SYNC_PLUGIN_NAME,\n} from './constants.js'\nimport { YHIKAS_ROOM_TYPE_ENTITY, YhikasRoomType } from './entities/room-type.js'\nimport { isRoomOffer, type RoomOfferPredicate } from './room-offers.js'\nimport type { RoomTypeReader } from './room-pages.js'\nimport type { RoomTypeForPage } from './room-type-view.js'\nimport type { SyncStateRecord } from './sync-state.js'\nimport { assessStaleness } from './watchdog.js'\n\n/** The answer `resolve` gives — a record of facts per room type code. */\ntype LiveAnswer = Awaited<ReturnType<LiveSourceSpec['resolve']>>\n\n/** The fact names this source answers. */\nexport const ROOM_FACT_NAMES = ['monthly', 'discounted', 'daily', 'places', 'area'] as const\nexport type RoomFactName = (typeof ROOM_FACT_NAMES)[number]\n\nconst PER_MONTH = { et: '/kuu', en: '/ month', ru: '/ мес.' } as const\n\n/** What a room answers — declared, so the panel offers them as checkboxes. */\nexport const ROOM_FACTS: readonly LiveFactSpec[] = [\n {\n name: 'monthly',\n label: { et: 'Kuuüür', en: 'Monthly rent', ru: 'Аренда в месяц' },\n format: 'price',\n unit: PER_MONTH,\n },\n { name: 'discounted', label: 'EKA/EMTA', format: 'price', unit: PER_MONTH },\n { name: 'daily', label: { et: 'päev', en: 'per day', ru: 'в день' }, format: 'price' },\n {\n name: 'places',\n label: { et: 'Kohti', en: 'Places', ru: 'Мест' },\n format: 'number',\n unit: { et: 'kohta', en: 'places', ru: 'мест' },\n },\n { name: 'area', label: { et: 'Pind', en: 'Area', ru: 'Площадь' }, format: 'number', unit: 'm²' },\n]\n\n/** The library field a facts source is granted by default — `liveFacts.values`. */\nexport const DEFAULT_ROOM_FACT_FIELDS: readonly string[] = ['liveFacts.values']\n\n/** The most room types one read answers — more than upstream has ever published (32). */\nconst MAX_ROOM_TYPES = 200\n\n/** How many items the picker lists — the toolkit's own ceiling for one source. */\nconst MAX_ITEMS = 50\n\n/**\n * A stored decimal string as a number, or `undefined` — never `NaN`, never a\n * negative, never a fabricated 0 for a missing value.\n */\nexport function decimalOf(raw: string | null): number | undefined {\n if (raw === null) return undefined\n const trimmed = raw.trim()\n // Plain decimals only — `Number()` would also accept `0x1f`, `1e3` and `Infinity`.\n if (!/^\\d+(\\.\\d+)?$/.test(trimmed)) return undefined\n const value = Number(trimmed)\n return Number.isFinite(value) ? value : undefined\n}\n\n/** One room type's facts — only the values it actually has. */\nexport function roomFactsOf(room: RoomTypeForPage): Partial<Record<RoomFactName, number>> {\n const facts: Partial<Record<RoomFactName, number>> = {}\n const monthly = decimalOf(room.monthlyRent)\n const discounted = decimalOf(room.discountedMonthlyRent)\n const daily = decimalOf(room.dailyRent)\n const area = decimalOf(room.totalArea)\n if (monthly !== undefined) facts.monthly = monthly\n if (discounted !== undefined) facts.discounted = discounted\n if (daily !== undefined) facts.daily = daily\n if (room.capacity !== null && room.capacity >= 0) facts.places = room.capacity\n if (area !== undefined) facts.area = area\n return facts\n}\n\n/** The pure half of `resolve`: the answer for `keys`, given the published rows. Unknown keys are absent. */\nexport function roomFactsFrom(\n rooms: readonly RoomTypeForPage[],\n keys: readonly string[],\n): LiveAnswer {\n const byCode = new Map(rooms.map((room) => [room.code, room]))\n const out: Record<string, LiveAnswer[string]> = {}\n for (const key of keys) {\n const room = byCode.get(key)\n if (room === undefined) continue\n const facts = roomFactsOf(room)\n // A room with no values answers nothing — \"no values\" draws exactly like\n // \"the room is gone\", which is what it is to a visitor.\n if (Object.keys(facts).length > 0) out[key] = { facts }\n }\n return out\n}\n\n/** The picker's items: offered rooms, by code, at most {@link MAX_ITEMS}. */\nexport function roomFactItemsFrom(\n rooms: readonly RoomTypeForPage[],\n offered: RoomOfferPredicate,\n): readonly LiveItem[] {\n return rooms\n .filter((room) => offered({ code: room.code, name: room.name }))\n .sort((a, b) => a.code.localeCompare(b.code))\n .slice(0, MAX_ITEMS)\n .map((room) => {\n const label = room.name.trim() === '' ? room.code : `${room.code} — ${room.name.trim()}`\n return { key: room.code, label: label.slice(0, 120) }\n })\n}\n\n/** Cadences with a translation key in this package's catalogue. */\nconst CADENCE_KEYS: Readonly<Record<string, string>> = {\n '@every 6h': 'YhikasSync.provenance.cadenceEvery6Hours',\n '@every 1h': 'YhikasSync.provenance.cadenceHourly',\n '@hourly': 'YhikasSync.provenance.cadenceHourly',\n '@every 12h': 'YhikasSync.provenance.cadenceEvery12Hours',\n '@every 24h': 'YhikasSync.provenance.cadenceDaily',\n '@every 1d': 'YhikasSync.provenance.cadenceDaily',\n '@daily': 'YhikasSync.provenance.cadenceDaily',\n}\n\nconst UNIT_WORDS: Readonly<Record<string, readonly [string, string]>> = {\n s: ['second', 'seconds'],\n m: ['minute', 'minutes'],\n h: ['hour', 'hours'],\n d: ['day', 'days'],\n}\n\nconst PREDEFINED: Readonly<Record<string, string>> = {\n '@hourly': 'hour',\n '@daily': 'day',\n '@weekly': 'week',\n '@monthly': 'month',\n}\n\n/**\n * A queue schedule as English words — `'@every 6h'` → `'every 6 hours'` —\n * plus a translation key when the catalogue has one for it. A cron expression\n * is named as such: its meaning is not worth a parser here.\n */\nexport function cadenceOf(schedule: string): { readonly text: string; readonly key?: string } {\n const key = CADENCE_KEYS[schedule]\n const interval = /^@every\\s+(\\d+)(s|m|h|d)$/.exec(schedule)\n let text: string\n if (interval !== null) {\n const count = Number(interval[1])\n const words = UNIT_WORDS[interval[2] ?? ''] ?? ['unit', 'units']\n text = count === 1 ? `every ${words[0]}` : `every ${count} ${words[1]}`\n } else if (PREDEFINED[schedule] !== undefined) {\n text = `every ${PREDEFINED[schedule]}`\n } else {\n text = `on a schedule (${schedule})`.slice(0, 120)\n }\n return key === undefined ? { text } : { text, key }\n}\n\nexport interface YhikasRoomFactsOptions {\n /** The source's English name in the panel. Default `'Room facts'`. */\n readonly label?: string\n /**\n * yhikas-admin's PUBLIC `https:` base URL — the panel links to\n * `${adminUrl}/admin/toad`, where room prices are changed (yhikas-admin has\n * no page per room). Omitted: no link.\n */\n readonly adminUrl?: string\n /**\n * The sync's schedule, for \"synced every 6 hours\". Default: the registered\n * sync plugin's own `schedule` — read when the panel asks, so the provenance\n * cannot drift from the job that actually runs — else `@every 6h`.\n */\n readonly schedule?: string\n /**\n * The staleness window. Default: the registered sync plugin's own\n * `staleAfterMs`, else 24 h — so the facts go stale exactly when the\n * watchdog alerts.\n */\n readonly staleAfterMs?: number\n /** Which rooms the picker lists. Default: the sync's `roomPages.offered`, else {@link isRoomOffer}. */\n readonly offered?: RoomOfferPredicate\n /**\n * Library fields this source may also be bound to — see\n * `LiveSourceSpec.fields`. Default {@link DEFAULT_ROOM_FACT_FIELDS}: the\n * library Facts block's `values`, so a site binds a room page's facts with\n * no further wiring. Pass `[]` to grant none.\n */\n readonly fields?: readonly string[]\n}\n\n/** The part of the registered sync plugin's config the facts read. */\nexport interface RegisteredSyncConfig {\n readonly schedule: string\n readonly staleAfterMs: number\n readonly roomPages?: { readonly offered: RoomOfferPredicate } | undefined\n}\n\n/** What the factory reads at RUN time — injectable, so each part is testable without an app. */\nexport interface RoomFactsRuntime {\n /** Published room types through the caller's own client. */\n readonly rooms: () => Promise<RoomTypeReader>\n /** The sync state, for freshness. */\n readonly syncState: () => Promise<readonly SyncStateRecord[]>\n /**\n * The registered sync plugin's resolved config, when it is registered.\n * Synchronous: `provenance` is a plain property the canvas reads without\n * awaiting.\n */\n readonly syncConfig: () => RegisteredSyncConfig | undefined\n readonly now: () => Date\n}\n\n/** The real runtime: the app singleton, read lazily. */\nconst appRuntime: RoomFactsRuntime = {\n async rooms() {\n const { getApp } = await import('@murumets-ee/core')\n const { toRoomTypeReader } = await import('./room-pages-adapters.js')\n const app = getApp()\n // The variant the sync plugin registered (same name, same fields) — what\n // the app actually serves; the shipped definition when it is not registered.\n const registered = app.entities.get(YHIKAS_ROOM_TYPE_ENTITY)\n const entity = registered === undefined ? YhikasRoomType : registered\n // No `defaultLocale`: it only steers localized BLOCK fallback, and a room\n // type has none — reading it would cost a settings query per render.\n return toRoomTypeReader(app.getClient(entity))\n },\n async syncState() {\n const { getApp } = await import('@murumets-ee/core')\n const { createSyncStateStore } = await import('./sync-state.js')\n const { yhikasSyncStateTable } = await import('./sync-state-table.js')\n return createSyncStateStore(yhikasSyncStateTable.makeClient(getApp().db.readOnly)).readAll()\n },\n syncConfig() {\n // Static import: `@murumets-ee/core`'s root is jiti-safe (the plugin entry\n // imports it too), and this must answer synchronously.\n return hasPluginConfig(YHIKAS_SYNC_PLUGIN_NAME)\n ? getPluginConfig(YHIKAS_SYNC_PLUGIN_NAME)\n : undefined\n },\n now: () => new Date(),\n}\n\n/** Room types' freshness, from the sync state — stale past the window, or never synced. */\nexport function roomTypesFreshness(\n records: readonly SyncStateRecord[],\n now: Date,\n staleAfterMs: number,\n): LiveFreshness {\n const room = records.find((record) => record.resource === ROOM_TYPES_RESOURCE)\n const assessed = assessStaleness(records, now, staleAfterMs).find(\n (entry) => entry.resource === ROOM_TYPES_RESOURCE,\n )\n const stale = room?.lastSuccessAt == null || assessed === undefined || assessed.stale\n return {\n ...(room?.lastSuccessAt != null ? { lastSyncedAt: room.lastSuccessAt.toISOString() } : {}),\n ...(stale ? { stale: true } : {}),\n }\n}\n\n/**\n * The synced room types as a live facts source. Keys are room type `code`s.\n *\n * Throws only for a structurally invalid `adminUrl` — at config load, where\n * the message can name the knob.\n */\nexport function yhikasRoomFacts(\n options: YhikasRoomFactsOptions = {},\n runtime: RoomFactsRuntime = appRuntime,\n): LiveSourceSpec {\n const adminUrl =\n options.adminUrl === undefined\n ? undefined\n : resolveAdminUrl(options.adminUrl, 'yhikasRoomFacts({ adminUrl })')\n return {\n label: options.label ?? 'Room facts',\n labelKey: 'YhikasSync.facts.sourceLabel',\n kind: 'facts',\n facts: ROOM_FACTS,\n // A getter, read each time the canvas asks: the sync plugin's config is\n // registered by `createApp`, AFTER this source was constructed in\n // `lumi.config.ts` — a value computed here would always be the default.\n get provenance(): LiveProvenance {\n const cadence = cadenceOf(\n options.schedule ?? runtime.syncConfig()?.schedule ?? DEFAULT_SYNC_SCHEDULE,\n )\n return {\n system: 'yhikas-admin',\n record: 'room type',\n recordKey: 'YhikasSync.provenance.recordRoomType',\n cadence: cadence.text,\n ...(cadence.key !== undefined ? { cadenceKey: cadence.key } : {}),\n ...(adminUrl !== undefined ? { editUrl: `${adminUrl}/admin/toad` } : {}),\n }\n },\n fields: options.fields ?? DEFAULT_ROOM_FACT_FIELDS,\n\n async resolve({ keys, locale }) {\n if (keys.length === 0) return {}\n const reader = await runtime.rooms()\n // One read per render whatever the number of keys — never a query per key.\n const rooms = await reader.published(locale, {\n limit: Math.min(keys.length, MAX_ROOM_TYPES),\n codes: keys,\n })\n return roomFactsFrom(rooms, keys)\n },\n\n async items({ locale }) {\n const offered = options.offered ?? runtime.syncConfig()?.roomPages?.offered ?? isRoomOffer\n const reader = await runtime.rooms()\n return roomFactItemsFrom(await reader.published(locale, { limit: MAX_ROOM_TYPES }), offered)\n },\n\n async freshness() {\n const staleAfterMs =\n options.staleAfterMs ?? runtime.syncConfig()?.staleAfterMs ?? DEFAULT_STALE_AFTER_MS\n return roomTypesFreshness(await runtime.syncState(), runtime.now(), staleAfterMs)\n },\n }\n}\n","/**\n * `recordRoomPage(code, pageId)` — for a site's own migration or seeder that\n * wrote a room's page itself: records that the room HAS its page, so the sync\n * never creates a draft for it (`yhikas_room_pages`, origin `registered`).\n *\n * Never overwrites: a code already recorded keeps its first page. Server-side\n * only — call it from a seeder or a `lumi` command, never from a route that\n * forwards request input.\n */\n\nimport { assertRecordable, createRoomPageRegistry } from './room-page-registry.js'\nimport { yhikasRoomPagesTable } from './room-pages-table.js'\n\n/**\n * @returns `true` when recorded; `false` when the code already has a page, or\n * a sync run is creating one right now (its claim is fresh).\n */\nexport async function recordRoomPage(code: string, pageId: string): Promise<boolean> {\n // Before the app is touched, so a bad argument fails with its own message.\n assertRecordable(code, pageId)\n const { getApp } = await import('@murumets-ee/core')\n const registry = createRoomPageRegistry(yhikasRoomPagesTable.makeClient(getApp().db.readWrite))\n return registry.record(code, pageId, 'registered', new Date())\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{E as e,T as t,g as n,l as r,p as i,t as a,x as o,y as s}from"./client-J_w85GUr.mjs";import{C as c,n as l,t as u,x as d}from"./constants-Bmm3P17G.mjs";import{i as f,n as p,s as m}from"./sync-state-table-CZjRypK0.mjs";import{a as h}from"./room-pages-table-Vd9BqTSl.mjs";import{a as g}from"./room-page-registry-BFbyGTC7.mjs";import{i as _,r as v}from"./room-pages-adapters-DxFWkFFF.mjs";import{elevateRequestContext as y,runWithContextAsync as b}from"@murumets-ee/core";var x=class extends Error{constructor(e){super(`yhikas-sync is enabled but ${e.join(` and `)} ${e.length===1?`is`:`are`} not set. Failing loudly rather than skipping quietly: an unconfigured sync is a broken sync, and a broken sync must be visibly broken. Set the variable, or pass \`enabled: false\` to yhikasSync() if this install has no yhikas-admin.`),this.name=`YhikasSyncNotConfiguredError`}};function S(){return y({user:{id:c,groups:[`admin`]}})}function C(e){return e.logger}async function w(e){let{resolveI18nConfig:n}=await import(`@murumets-ee/content`),{defaultLocale:r}=await n(e);if(!o.includes(r))throw new T(`The app's default locale is '${r}', but yhikas-admin carries content in 'et' and 'en' only. The sync has nothing to write to the base row, so it refuses rather than publishing one language under another's name.`);return{defaultLocale:r,secondaryLocale:t(r)}}var T=class extends Error{constructor(e){super(e),this.name=`YhikasSyncLocaleError`}};function E(e,t){let n=t[u],r=[];if(e.baseUrl||r.push(l),n||r.push(u),!e.baseUrl||!n)throw new x(r);return{baseUrl:e.baseUrl,apiKey:n}}async function D(t,n,o=process.env){let{baseUrl:s,apiKey:c}=E(n,o),l=f(n),{sanitizeHtml:u}=await import(`@murumets-ee/blocks`);return b(S(),async()=>{let{defaultLocale:o}=await w(t);return i({upstream:new a({baseUrl:s,apiKey:c,timeoutMs:n.requestTimeoutMs}),roomTypeClient:e(t.getClient(l.roomType),o),legalDocumentClient:e(t.getClient(l.legalDocument),o),siteInfoClient:e(t.getClient(m),o),state:r(p.makeClient(t.db.readWrite)),sanitizeHtml:u,logger:C(t),defaultLocale:o,floor:{maxRows:n.maxRowsPerResource,maxRetireFraction:n.maxRetireFraction,minRowsForFraction:n.minRowsForRetireFloor},...n.roomPages===void 0?{}:{afterRoomTypesSynced:()=>O(t,n,o,l.roomType)}})})}async function O(e,t,r,i){let a=t.roomPages;if(a===void 0)return;let o=e.entities.get(a.entity);if(o===void 0){e.logger.warn({entity:a.entity},`yhikas-sync: roomPages names an entity this app does not register — no room pages created`);return}let{enabledLocaleCodes:s,resolveEffectiveLocales:c}=await import(`@murumets-ee/content`),l=s(await c(e));await n({config:a,rooms:_(e.getClient(i),r),pages:v(e.getClient(o),r,C(e)),registry:g(h.makeClient(e.db.readWrite)),logger:C(e),defaultLocale:r,locales:l,maxRows:t.maxRowsPerResource})}async function k(e,t,n=new Date){let i=r(p.makeClient(e.db.readWrite));await A(e,n);let a=s(await i.readAll(),n,t.staleAfterMs);return e.logger.debug({window:t.staleAfterMs,resources:a.map(e=>({resource:e.resource,lastSuccessAt:e.lastSuccessAt}))},`yhikas-sync: staleness check passed`),a}async function A(e,t=new Date){let n=r(p.makeClient(e.db.readWrite));for(let e of d)await n.ensure(e,t)}export{D as runSyncJob,k as runWatchdogJob};
2
+ //# sourceMappingURL=jobs-Di4Z8go9.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs-Di4Z8go9.mjs","names":[],"sources":["../src/jobs.ts"],"sourcesContent":["/**\n * The two job handlers, bound to a live `ToolkitApp`.\n *\n * This module is reached ONLY through a dynamic `import()` from the plugin's\n * `init` (see `plugin.ts`). It statically imports `@murumets-ee/core` and\n * `@murumets-ee/blocks`, either of which would be evaluated at\n * `lumi.config.ts`-load time under the CLI's jiti loader if the plugin entry\n * reached it statically — and `@murumets-ee/blocks`' root export evaluates\n * React's `createContext` at module scope.\n */\n\nimport { elevateRequestContext, runWithContextAsync, type ToolkitApp } from '@murumets-ee/core'\nimport type { SyncLogger } from './apply.js'\nimport type { ResolvedYhikasSyncConfig } from './config.js'\nimport {\n API_KEY_ENV_VAR,\n BASE_URL_ENV_VAR,\n EN_LOCALE,\n ET_LOCALE,\n LEGAL_DOCUMENTS_RESOURCE,\n ROOM_TYPES_RESOURCE,\n SYNC_RESOURCES,\n YHIKAS_SYNC_ACTOR_ID,\n} from './constants.js'\nimport { detailPageEntities } from './entities/index.js'\nimport { YhikasSiteInfo } from './entities/site-info.js'\nimport { toSyncEntityClient } from './entity-client.js'\nimport { SUPPORTED_LOCALES, secondaryLocaleOf } from './projection.js'\nimport { createRoomPageRegistry } from './room-page-registry.js'\nimport { createRoomPageDrafts } from './room-pages.js'\nimport { toRoomPageWriter, toRoomTypeReader } from './room-pages-adapters.js'\nimport { yhikasRoomPagesTable } from './room-pages-table.js'\nimport { runYhikasSync, type SyncRunSummary } from './run-sync.js'\nimport { createSyncStateStore } from './sync-state.js'\nimport { yhikasSyncStateTable } from './sync-state-table.js'\nimport { YhikasUpstreamClient } from './upstream/client.js'\nimport { assertNotStale, type ResourceStaleness } from './watchdog.js'\n\n/** Thrown when the sync is enabled but has no credential or no origin to talk to. */\nexport class YhikasSyncNotConfiguredError extends Error {\n constructor(missing: readonly string[]) {\n super(\n `yhikas-sync is enabled but ${missing.join(' and ')} ${missing.length === 1 ? 'is' : 'are'} ` +\n `not set. Failing loudly rather than skipping quietly: an unconfigured sync is a broken ` +\n `sync, and a broken sync must be visibly broken. Set the variable, or pass ` +\n `\\`enabled: false\\` to yhikasSync() if this install has no yhikas-admin.`,\n )\n this.name = 'YhikasSyncNotConfiguredError'\n }\n}\n\n/**\n * Every entity write runs as this identity.\n *\n * `elevateRequestContext` rather than `runAsCli`: both replace the permission\n * checker, but this one carries a NAMED actor, so `auditable()` records\n * `yhikas-sync` in `created_by`/`updated_by` instead of a generic `cli`. That\n * makes the sync's writes attributable — worth having precisely because the\n * upstream writes are not (F006) — and it is what the machine-written guard\n * recognises when it refuses every other writer.\n *\n * Built per run rather than at module scope: `elevateRequestContext` reads the\n * ambient context at CALL time, and a module-scope constant would bake in\n * whatever context happened to exist when the module first loaded.\n */\nfunction syncContext() {\n return elevateRequestContext({\n user: { id: YHIKAS_SYNC_ACTOR_ID, groups: ['admin'] },\n })\n}\n\n/** Narrow `app.logger` to the shape the sync uses, without importing a logger type. */\nfunction toSyncLogger(app: ToolkitApp): SyncLogger {\n return app.logger\n}\n\n/**\n * The app's REAL default locale, read from the content plugin rather than\n * configured here.\n *\n * A configured copy could disagree with the app's actual default, and the\n * failure would be silent in both directions: `updateForLocale` would take its\n * `isDefault` branch for what this package thought was the secondary locale\n * and overwrite the base row with the wrong language, while no translation was\n * ever written for the real secondary. `resolveI18nConfig` is the authoritative\n * answer — it merges the runtime `i18n` override over the shipped config — so\n * there is nothing to keep in sync.\n */\nasync function resolveLocales(\n app: ToolkitApp,\n): Promise<{ defaultLocale: string; secondaryLocale: string }> {\n const { resolveI18nConfig } = await import('@murumets-ee/content')\n const { defaultLocale } = await resolveI18nConfig(app)\n\n if (!SUPPORTED_LOCALES.includes(defaultLocale)) {\n throw new YhikasSyncLocaleError(\n `The app's default locale is '${defaultLocale}', but yhikas-admin carries content in ` +\n `'${ET_LOCALE}' and '${EN_LOCALE}' only. The sync has nothing to write to the base row, ` +\n `so it refuses rather than publishing one language under another's name.`,\n )\n }\n // `secondaryLocaleOf` asserts the same thing, so the two can never disagree.\n return { defaultLocale, secondaryLocale: secondaryLocaleOf(defaultLocale) }\n}\n\n/** Thrown when the app's locale configuration cannot carry the synced content. */\nexport class YhikasSyncLocaleError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'YhikasSyncLocaleError'\n }\n}\n\n/**\n * Resolve the bearer credential at RUN time, from the environment.\n *\n * Deliberately not part of the plugin's declared config: resolved config lands\n * in the framework's `PluginConfigRegistry`, a process-global any plugin can\n * read, and a secret does not belong there (mirroring `@murumets-ee/merit`'s\n * D003). Reading it here also means rotating the key needs a restart rather\n * than a rebuild.\n */\nfunction resolveCredentials(\n config: ResolvedYhikasSyncConfig,\n env: NodeJS.ProcessEnv,\n): { baseUrl: string; apiKey: string } {\n const apiKey = env[API_KEY_ENV_VAR]\n const missing: string[] = []\n if (!config.baseUrl) missing.push(BASE_URL_ENV_VAR)\n if (!apiKey) missing.push(API_KEY_ENV_VAR)\n if (!config.baseUrl || !apiKey) throw new YhikasSyncNotConfiguredError(missing)\n return { baseUrl: config.baseUrl, apiKey }\n}\n\n/** One full sync run. Throws on any resource failure, so the queue retries and alerts. */\nexport async function runSyncJob(\n app: ToolkitApp,\n config: ResolvedYhikasSyncConfig,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<SyncRunSummary> {\n const { baseUrl, apiKey } = resolveCredentials(config, env)\n const detailEntities = detailPageEntities(config)\n const { sanitizeHtml } = await import('@murumets-ee/blocks')\n\n return runWithContextAsync(syncContext(), async () => {\n const { defaultLocale } = await resolveLocales(app)\n return runYhikasSync({\n upstream: new YhikasUpstreamClient({\n baseUrl,\n apiKey,\n timeoutMs: config.requestTimeoutMs,\n }),\n // The clients are built HERE, inside the job, not in the plugin's\n // `init`. That is load-bearing and easy to \"tidy\" into a bug:\n // `createAdminClient` reads the locale-status writer ONCE at\n // construction, and `@murumets-ee/content` installs that writer from its\n // own `init`. `Plugin.requires` guarantees content is CONFIGURED, not\n // that it initialised first — so a client constructed during init could\n // capture no writer, and `updateForLocale` would then silently assign\n // the requested status to the BASE row. Constructing at job-run time is\n // after every plugin's init, unconditionally.\n // The definitions the plugin REGISTERED — the hand-built variants when\n // the site chose them (`detailPages`) — so the sync writes through the\n // entity the app actually serves.\n roomTypeClient: toSyncEntityClient(app.getClient(detailEntities.roomType), defaultLocale),\n legalDocumentClient: toSyncEntityClient(\n app.getClient(detailEntities.legalDocument),\n defaultLocale,\n ),\n siteInfoClient: toSyncEntityClient(app.getClient(YhikasSiteInfo), defaultLocale),\n state: createSyncStateStore(yhikasSyncStateTable.makeClient(app.db.readWrite)),\n sanitizeHtml,\n logger: toSyncLogger(app),\n defaultLocale,\n floor: {\n maxRows: config.maxRowsPerResource,\n maxRetireFraction: config.maxRetireFraction,\n minRowsForFraction: config.minRowsForRetireFloor,\n },\n ...(config.roomPages !== undefined\n ? {\n afterRoomTypesSynced: () =>\n runRoomPageDrafts(app, config, defaultLocale, detailEntities.roomType),\n }\n : {}),\n })\n })\n}\n\n/**\n * The room-page drafts (`roomPages`), after a successful room-types sync.\n *\n * ## ⚠️ This WIDENS what the sync's elevation reaches — stated, not hidden\n *\n * It runs INSIDE the sync's own context: `elevateRequestContext` for the named\n * `yhikas-sync` actor (`syncContext()` above), i.e. FULL authority. Before\n * `roomPages`, that authority only ever wrote the three `yhikas_*` entities\n * this package owns. With `roomPages` it also writes the site's `page` entity\n * (create, and `updateForLocale` of title / layout / slug), so the identity\n * enumerated in `ELEVATION_ALLOWLIST` (`elevateRequestContext`) now covers a\n * write to a CONTENT entity.\n *\n * No sanctioned constructor narrows it: `createSystemReachResolver` grants\n * `view` only, `runAsPublicReader` is a public reader's identity and refuses\n * to run inside a context with a user, and `createSystemAdminClient` is the\n * same full authority again. A hand-built context with a narrow checker would\n * narrow it, but it is exactly the shape `verify:boundaries` cannot see, so it\n * would trade a stated widening for an invisible one.\n *\n * What bounds it, instead:\n * - every value written is DERIVED server-side — the code (slug), the parent\n * id found by `parentSlug` from config, and the site's own `draft()`; none\n * is forwarded request input;\n * - the write is `create` + per-locale content, never `status`: nothing is\n * ever published, overwritten after its first write, or deleted;\n * - at most `MAX_ROOM_PAGES_PER_RUN` pages per run, each claimed once, ever;\n * - pages are attributed to `yhikas-sync` in `created_by` and the audit log;\n * - only when a site opts in with `roomPages`.\n */\nasync function runRoomPageDrafts(\n app: ToolkitApp,\n config: ResolvedYhikasSyncConfig,\n defaultLocale: string,\n roomType: ReturnType<typeof detailPageEntities>['roomType'],\n): Promise<void> {\n const roomPages = config.roomPages\n if (roomPages === undefined) return\n const pageEntity = app.entities.get(roomPages.entity)\n if (pageEntity === undefined) {\n app.logger.warn(\n { entity: roomPages.entity },\n 'yhikas-sync: roomPages names an entity this app does not register — no room pages created',\n )\n return\n }\n const { enabledLocaleCodes, resolveEffectiveLocales } = await import('@murumets-ee/content')\n const locales = enabledLocaleCodes(await resolveEffectiveLocales(app))\n await createRoomPageDrafts({\n config: roomPages,\n rooms: toRoomTypeReader(app.getClient(roomType), defaultLocale),\n pages: toRoomPageWriter(app.getClient(pageEntity), defaultLocale, toSyncLogger(app)),\n registry: createRoomPageRegistry(yhikasRoomPagesTable.makeClient(app.db.readWrite)),\n logger: toSyncLogger(app),\n defaultLocale,\n locales,\n maxRows: config.maxRowsPerResource,\n })\n}\n\n/**\n * The staleness check. Throws when any resource is past the window, which is\n * how it reaches the queue's dead-letter alerting — the detection is new, the\n * delivery is not.\n */\nexport async function runWatchdogJob(\n app: ToolkitApp,\n config: ResolvedYhikasSyncConfig,\n now: Date = new Date(),\n): Promise<ResourceStaleness[]> {\n const store = createSyncStateStore(yhikasSyncStateTable.makeClient(app.db.readWrite))\n // Seed before assessing, so a fresh install gets a `firstSeenAt` to be\n // measured from rather than reading as \"no tracking row at all\" — which is\n // correct but alarming on an app that simply has not reached its first sync\n // tick yet.\n //\n // The cost, stated: `assessStaleness`'s missing-row branch is therefore\n // unreachable from here, so a tracking row DELETED by hand while the sync is\n // broken resets the clock instead of alerting. That needs deliberate DB\n // surgery to reach, and the alternative — alerting on every fresh install's\n // first tick — trains people to ignore the one channel that has to stay\n // trustworthy.\n await ensureSyncState(app, now)\n const records = await store.readAll()\n const assessed = assertNotStale(records, now, config.staleAfterMs)\n app.logger.debug(\n {\n window: config.staleAfterMs,\n resources: assessed.map((entry) => ({\n resource: entry.resource,\n lastSuccessAt: entry.lastSuccessAt,\n })),\n },\n 'yhikas-sync: staleness check passed',\n )\n return assessed\n}\n\n/**\n * Seed a tracking row per resource, so a fresh install has a clock to be\n * measured against before its first sync tick.\n *\n * Called from the WATCHDOG, not from the plugin's `init`. Doing DB I/O during\n * `init` runs it in every process that builds a `ToolkitApp` — web included —\n * and would fail app boot outright on a database blip, or on any boot that\n * precedes `lumi migrate` creating the table, in exchange for two bookkeeping\n * rows.\n */\nexport async function ensureSyncState(app: ToolkitApp, now: Date = new Date()): Promise<void> {\n const store = createSyncStateStore(yhikasSyncStateTable.makeClient(app.db.readWrite))\n for (const resource of SYNC_RESOURCES) {\n await store.ensure(resource, now)\n }\n}\n\nexport { LEGAL_DOCUMENTS_RESOURCE, ROOM_TYPES_RESOURCE }\n"],"mappings":"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"}
package/dist/plugin.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as YhikasSyncConfig, t as ResolvedYhikasSyncConfig } from "./config-ClDwmosW.mjs";
1
+ import { a as RoomPageDraft, g as isRoomOffer, i as RoomPageBlock, n as ResolvedRoomPagesConfig, o as YhikasRoomPagesConfig, r as ResolvedYhikasSyncConfig, s as YhikasSyncConfig, u as RoomTypeForPage } from "./config-DM3zAAhm.mjs";
2
2
 
3
3
  //#region src/plugin.d.ts
4
4
  declare module '@murumets-ee/core' {
@@ -16,10 +16,12 @@ declare module '@murumets-ee/core' {
16
16
  * export default defineLumiConfig({ plugins: [content({...}), queue(), yhikasSync()] })
17
17
  * ```
18
18
  */
19
- declare function yhikasSync(config?: YhikasSyncConfig): Pick<import("@murumets-ee/core").Plugin, "server" | "adminUi" | "postInstall" | "requires" | "contributes"> & {
19
+ declare function yhikasSync(config?: YhikasSyncConfig): Pick<import("@murumets-ee/core").Plugin, "adminUi" | "server" | "postInstall" | "requires" | "contributes"> & {
20
20
  readonly name: "@murumets-ee/yhikas-sync";
21
- readonly shared?: undefined;
21
+ readonly shared?: {
22
+ readonly messages: (locale: string) => Promise<Record<string, unknown>>;
23
+ };
22
24
  };
23
25
  //#endregion
24
- export { type ResolvedYhikasSyncConfig, type YhikasSyncConfig, yhikasSync };
26
+ export { type ResolvedRoomPagesConfig, type ResolvedYhikasSyncConfig, type RoomPageBlock, type RoomPageDraft, type RoomTypeForPage, type YhikasRoomPagesConfig, type YhikasSyncConfig, isRoomOffer, yhikasSync };
25
27
  //# sourceMappingURL=plugin.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;YAwBY,oBAAA;IACR,0BAAA,EAA4B,wBAAwB;EAAA;AAAA;;;;;;;;;;;iBAcxC,UAAA,CAAW,MAAA,GAAS,gBAAA,GAAgB,IAAA,6BAAA,MAAA;EAAA"}
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;YAoDY,oBAAA;IACR,0BAAA,EAA4B,wBAAwB;EAAA;AAAA;;;;;;;;;;;iBAcxC,UAAA,CAAW,MAAA,GAAS,gBAAA,GAAgB,IAAA,6BAAA,MAAA;EAAA;;2CAjCN,OAAA,CAAQ,MAAA;EAAA;AAAA"}
package/dist/plugin.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{A as e,M as t,P as n,h as r,i,t as a}from"./sync-state-table-Dq_wyKfo.mjs";import{definePlugin as o}from"@murumets-ee/core";function s(s){let c=r(s);return o({name:n,requires:[`@murumets-ee/content`,`@murumets-ee/queue`],server:{config:c,entities:[...i(c.detailPages)],tables:{yhikas_sync_state:a.table},init:async r=>{if(!c.enabled){r.logger.warn({plugin:n},`yhikas-sync is disabled by config — no scheduled pull, and no staleness alerting`);return}let{defineJob:i,registerJob:a}=await import(`@murumets-ee/queue/client`);a(i({name:e,description:`Pull room types and legal documents from yhikas-admin and apply a full-snapshot diff.`,schedule:c.schedule}),async()=>{let{runSyncJob:e}=await import(`./jobs-Vg-eMVGf.mjs`);await e(r,c)}),a(i({name:t,description:`Alert when the newest successful yhikas-admin sync is older than the staleness window.`,schedule:c.watchdogSchedule}),async()=>{let{runWatchdogJob:e}=await import(`./jobs-Vg-eMVGf.mjs`);await e(r,c)})}}})}export{s as yhikasSync};
1
+ import{S as e,b as t,w as n}from"./constants-Bmm3P17G.mjs";import{a as r,h as i,n as a,p as o}from"./sync-state-table-CZjRypK0.mjs";import{a as s}from"./room-pages-table-Vd9BqTSl.mjs";import{definePlugin as c}from"@murumets-ee/core";import{getYhikasSyncMessages as l}from"@murumets-ee/yhikas-sync/i18n";const u=l;async function d(e){return(await import(`@murumets-ee/yhikas-sync/widgets`)).RoomPagesWidget(e)}function f(i){let l=o(i);return c({name:n,requires:[`@murumets-ee/content`,`@murumets-ee/queue`],server:{config:l,entities:[...r(l)],tables:{yhikas_sync_state:a.table,yhikas_room_pages:s.table},init:async r=>{if(!l.enabled){r.logger.warn({plugin:n},`yhikas-sync is disabled by config — no scheduled pull, and no staleness alerting`);return}let{defineJob:i,registerJob:a}=await import(`@murumets-ee/queue/client`);a(i({name:t,description:`Pull room types and legal documents from yhikas-admin and apply a full-snapshot diff.`,schedule:l.schedule}),async()=>{let{runSyncJob:e}=await import(`./jobs-Di4Z8go9.mjs`);await e(r,l)}),a(i({name:e,description:`Alert when the newest successful yhikas-admin sync is older than the staleness window.`,schedule:l.watchdogSchedule}),async()=>{let{runWatchdogJob:e}=await import(`./jobs-Di4Z8go9.mjs`);await e(r,l)})}},shared:{messages:u},...l.roomPages===void 0?{}:{adminUi:{dashboardWidgets:[{id:`yhikas-room-pages`,component:d,order:10}]}}})}export{i as isRoomOffer,f as yhikasSync};
2
2
  //# sourceMappingURL=plugin.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * `@murumets-ee/yhikas-sync/plugin` — the jiti-safe plugin entry.\n *\n * Apps wire the sync by importing `yhikasSync()` into their `lumi.config.ts`,\n * which the `lumi` CLI loads through jiti. A static `import 'server-only'`\n * anywhere in THIS module's top-level graph throws under plain Node and breaks\n * every `lumi` command (see `gotcha_server_only_in_cli_load_path`), so the\n * static imports here are limited to pure schema and config modules. The job\n * bodies — which reach `@murumets-ee/core`, `@murumets-ee/blocks` and the\n * entity admin client — are behind a dynamic `import()` that only runs when a\n * worker actually invokes a handler.\n */\n\nimport { definePlugin, type ToolkitApp } from '@murumets-ee/core'\nimport {\n type ResolvedYhikasSyncConfig,\n resolveYhikasSyncConfig,\n type YhikasSyncConfig,\n} from './config.js'\nimport { SYNC_JOB_NAME, WATCHDOG_JOB_NAME, YHIKAS_SYNC_PLUGIN_NAME } from './constants.js'\nimport { syncedEntities } from './entities/index.js'\nimport { yhikasSyncStateTable } from './sync-state-table.js'\n\ndeclare module '@murumets-ee/core' {\n interface PluginConfigRegistry {\n '@murumets-ee/yhikas-sync': ResolvedYhikasSyncConfig\n }\n}\n\n/**\n * The yhikas-admin sync plugin.\n *\n * @example\n * ```ts\n * // lumi.config.ts\n * import { yhikasSync } from '@murumets-ee/yhikas-sync/plugin'\n * export default defineLumiConfig({ plugins: [content({...}), queue(), yhikasSync()] })\n * ```\n */\nexport function yhikasSync(config?: YhikasSyncConfig) {\n const resolved = resolveYhikasSyncConfig(config)\n\n return definePlugin({\n name: YHIKAS_SYNC_PLUGIN_NAME,\n\n /**\n * `@murumets-ee/content` is a hard requirement because without it there is\n * no locale-status writer: `createAdminClient` finds none, and\n * `updateForLocale`'s fallback assigns the requested status to the BASE\n * row — so unpublishing the English locale would silently unpublish the\n * Estonian one.\n *\n * **What `requires` does and does not buy.** The framework validates it at\n * the start of `createApp`, before any init runs, but the check is\n * PRESENCE-ONLY — its own throw message says \"plugin-array order does not\n * matter\". It cannot guarantee that content's `init` has already installed\n * the writer.\n *\n * The ordering half is bought elsewhere, and it is the easier one to break\n * by accident: `readActiveLocaleStatusWriter` runs once at AdminClient\n * CONSTRUCTION, and this package constructs its clients inside the job\n * handler (see `jobs.ts#runSyncJob`), which is after every plugin's init\n * unconditionally. Hoisting that construction into `init` — a natural-\n * looking tidy-up, since the queue wiring already lives here — would\n * reintroduce the exact hazard while leaving this declaration green.\n *\n * `@murumets-ee/queue` is required for the obvious reason: without it there\n * is no scheduler and the sync would never run.\n */\n requires: ['@murumets-ee/content', '@murumets-ee/queue'],\n\n server: {\n config: resolved,\n // The site's choice of who draws the detail pages — see `detailPages`.\n entities: [...syncedEntities(resolved.detailPages)],\n // The Drizzle table, not the `defineTable` wrapper: the framework's merge\n // step calls `schemaRegistry.register(getTableName(table), table)` on\n // each entry. Declaring it here (rather than registering from `init`)\n // means it is registered BEFORE any init runs — which is what `lumi\n // migrate` needs in order to see it in the runtime schema diff.\n tables: { yhikas_sync_state: yhikasSyncStateTable.table },\n\n /**\n * `init` is the ONE sanctioned imperative slot — queue handler\n * registration (CLAUDE.md's carve-out). There is no `Plugin.server.jobs`\n * field to declare into; every consumer in this repo does exactly this.\n */\n init: async (app: ToolkitApp) => {\n if (!resolved.enabled) {\n app.logger.warn(\n { plugin: YHIKAS_SYNC_PLUGIN_NAME },\n 'yhikas-sync is disabled by config — no scheduled pull, and no staleness alerting',\n )\n return\n }\n\n const { defineJob, registerJob } = await import('@murumets-ee/queue/client')\n\n const syncJob = defineJob({\n name: SYNC_JOB_NAME,\n description:\n 'Pull room types and legal documents from yhikas-admin and apply a full-snapshot diff.',\n schedule: resolved.schedule,\n })\n\n registerJob(syncJob, async () => {\n const { runSyncJob } = await import('./jobs.js')\n // Rethrown deliberately, unlike `toolkit:trash-purge`'s swallow: a\n // failed sync must count against `maxRetries` and reach the\n // dead-letter alerter. A partial sweep of trashed rows self-heals\n // next tick; a site serving last month's prices does not announce\n // itself.\n await runSyncJob(app, resolved)\n })\n\n const watchdogJob = defineJob({\n name: WATCHDOG_JOB_NAME,\n description:\n 'Alert when the newest successful yhikas-admin sync is older than the staleness window.',\n schedule: resolved.watchdogSchedule,\n })\n\n registerJob(watchdogJob, async () => {\n const { runWatchdogJob } = await import('./jobs.js')\n // Throwing IS the alert: the queue's alerter fires only from\n // `failJob`'s dead-letter branch, so this is how a check that\n // detects ABSENCE of success reaches a channel that only knows how\n // to report presence of an error.\n await runWatchdogJob(app, resolved)\n })\n },\n },\n })\n}\n\nexport type { ResolvedYhikasSyncConfig, YhikasSyncConfig }\n"],"mappings":"mIAuCA,SAAgB,EAAW,EAA2B,CACpD,IAAM,EAAW,EAAwB,CAAM,EAE/C,OAAO,EAAa,CAClB,KAAM,EA0BN,SAAU,CAAC,uBAAwB,oBAAoB,EAEvD,OAAQ,CACN,OAAQ,EAER,SAAU,CAAC,GAAG,EAAe,EAAS,WAAW,CAAC,EAMlD,OAAQ,CAAE,kBAAmB,EAAqB,KAAM,EAOxD,KAAM,KAAO,IAAoB,CAC/B,GAAI,CAAC,EAAS,QAAS,CACrB,EAAI,OAAO,KACT,CAAE,OAAQ,CAAwB,EAClC,kFACF,EACA,MACF,CAEA,GAAM,CAAE,YAAW,eAAgB,MAAM,OAAO,6BAShD,EAPgB,EAAU,CACxB,KAAM,EACN,YACE,wFACF,SAAU,EAAS,QACrB,CAEkB,EAAG,SAAY,CAC/B,GAAM,CAAE,cAAe,MAAM,OAAO,uBAMpC,MAAM,EAAW,EAAK,CAAQ,CAChC,CAAC,EASD,EAPoB,EAAU,CAC5B,KAAM,EACN,YACE,yFACF,SAAU,EAAS,gBACrB,CAEsB,EAAG,SAAY,CACnC,GAAM,CAAE,kBAAmB,MAAM,OAAO,uBAKxC,MAAM,EAAe,EAAK,CAAQ,CACpC,CAAC,CACH,CACF,CACF,CAAC,CACH"}
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * `@murumets-ee/yhikas-sync/plugin` — the jiti-safe plugin entry.\n *\n * Apps wire the sync by importing `yhikasSync()` into their `lumi.config.ts`,\n * which the `lumi` CLI loads through jiti. A static `import 'server-only'`\n * anywhere in THIS module's top-level graph throws under plain Node and breaks\n * every `lumi` command (see `gotcha_server_only_in_cli_load_path`), so the\n * static imports here are limited to pure schema and config modules. The job\n * bodies — which reach `@murumets-ee/core`, `@murumets-ee/blocks` and the\n * entity admin client — are behind a dynamic `import()` that only runs when a\n * worker actually invokes a handler.\n */\n\nimport { definePlugin, type ToolkitApp } from '@murumets-ee/core'\n// `neverBundle`'d on this entry — a SELF-reference, so the loader's\n// `import('./messages/et.json')` resolves against the `i18n` chunk's own dist.\nimport { getYhikasSyncMessages } from '@murumets-ee/yhikas-sync/i18n'\nimport type { RoomPagesWidgetProps } from '@murumets-ee/yhikas-sync/widgets'\nimport type { ReactElement } from 'react'\nimport {\n type ResolvedYhikasSyncConfig,\n resolveYhikasSyncConfig,\n type YhikasSyncConfig,\n} from './config.js'\nimport { SYNC_JOB_NAME, WATCHDOG_JOB_NAME, YHIKAS_SYNC_PLUGIN_NAME } from './constants.js'\nimport { syncedEntities } from './entities/index.js'\nimport { yhikasRoomPagesTable } from './room-pages-table.js'\nimport { yhikasSyncStateTable } from './sync-state-table.js'\n\n/**\n * `getYhikasSyncMessages`, annotated — a SELF-reference import emits\n * `readonly messages: any` in the published `d.mts` otherwise (see\n * `@murumets-ee/queue-ui`'s `queueUiMessages`).\n */\nconst yhikasSyncMessages: (locale: string) => Promise<Record<string, unknown>> =\n getYhikasSyncMessages\n\n/**\n * The room pages dashboard widget, behind a lazy import.\n *\n * The plugin entry is loaded by the CLI through jiti at `lumi.config.ts`\n * time, where neither React nor `next-intl/server` may be evaluated. So the\n * contributed component is this thin async server component, and the widget\n * module — JSX, the client island's reference, the DB reads — loads only when\n * the dashboard renders it, in RSC.\n */\nasync function RoomPagesWidget(props: RoomPagesWidgetProps): Promise<ReactElement | null> {\n const widgets = await import('@murumets-ee/yhikas-sync/widgets')\n return widgets.RoomPagesWidget(props)\n}\n\ndeclare module '@murumets-ee/core' {\n interface PluginConfigRegistry {\n '@murumets-ee/yhikas-sync': ResolvedYhikasSyncConfig\n }\n}\n\n/**\n * The yhikas-admin sync plugin.\n *\n * @example\n * ```ts\n * // lumi.config.ts\n * import { yhikasSync } from '@murumets-ee/yhikas-sync/plugin'\n * export default defineLumiConfig({ plugins: [content({...}), queue(), yhikasSync()] })\n * ```\n */\nexport function yhikasSync(config?: YhikasSyncConfig) {\n const resolved = resolveYhikasSyncConfig(config)\n\n return definePlugin({\n name: YHIKAS_SYNC_PLUGIN_NAME,\n\n /**\n * `@murumets-ee/content` is a hard requirement because without it there is\n * no locale-status writer: `createAdminClient` finds none, and\n * `updateForLocale`'s fallback assigns the requested status to the BASE\n * row — so unpublishing the English locale would silently unpublish the\n * Estonian one.\n *\n * **What `requires` does and does not buy.** The framework validates it at\n * the start of `createApp`, before any init runs, but the check is\n * PRESENCE-ONLY — its own throw message says \"plugin-array order does not\n * matter\". It cannot guarantee that content's `init` has already installed\n * the writer.\n *\n * The ordering half is bought elsewhere, and it is the easier one to break\n * by accident: `readActiveLocaleStatusWriter` runs once at AdminClient\n * CONSTRUCTION, and this package constructs its clients inside the job\n * handler (see `jobs.ts#runSyncJob`), which is after every plugin's init\n * unconditionally. Hoisting that construction into `init` — a natural-\n * looking tidy-up, since the queue wiring already lives here — would\n * reintroduce the exact hazard while leaving this declaration green.\n *\n * `@murumets-ee/queue` is required for the obvious reason: without it there\n * is no scheduler and the sync would never run.\n */\n requires: ['@murumets-ee/content', '@murumets-ee/queue'],\n\n server: {\n config: resolved,\n // The site's choice of who draws the detail pages — see `detailPages`.\n entities: [...syncedEntities(resolved)],\n // The Drizzle table, not the `defineTable` wrapper: the framework's merge\n // step calls `schemaRegistry.register(getTableName(table), table)` on\n // each entry. Declaring it here (rather than registering from `init`)\n // means it is registered BEFORE any init runs — which is what `lumi\n // migrate` needs in order to see it in the runtime schema diff.\n tables: {\n yhikas_sync_state: yhikasSyncStateTable.table,\n // Declared whether or not `roomPages` is set: a table that appears and\n // disappears with a config flag would make `lumi migrate` drop it —\n // and with it the record of which rooms already had their page.\n yhikas_room_pages: yhikasRoomPagesTable.table,\n },\n\n /**\n * `init` is the ONE sanctioned imperative slot — queue handler\n * registration (CLAUDE.md's carve-out). There is no `Plugin.server.jobs`\n * field to declare into; every consumer in this repo does exactly this.\n */\n init: async (app: ToolkitApp) => {\n if (!resolved.enabled) {\n app.logger.warn(\n { plugin: YHIKAS_SYNC_PLUGIN_NAME },\n 'yhikas-sync is disabled by config — no scheduled pull, and no staleness alerting',\n )\n return\n }\n\n const { defineJob, registerJob } = await import('@murumets-ee/queue/client')\n\n const syncJob = defineJob({\n name: SYNC_JOB_NAME,\n description:\n 'Pull room types and legal documents from yhikas-admin and apply a full-snapshot diff.',\n schedule: resolved.schedule,\n })\n\n registerJob(syncJob, async () => {\n const { runSyncJob } = await import('./jobs.js')\n // Rethrown deliberately, unlike `toolkit:trash-purge`'s swallow: a\n // failed sync must count against `maxRetries` and reach the\n // dead-letter alerter. A partial sweep of trashed rows self-heals\n // next tick; a site serving last month's prices does not announce\n // itself.\n await runSyncJob(app, resolved)\n })\n\n const watchdogJob = defineJob({\n name: WATCHDOG_JOB_NAME,\n description:\n 'Alert when the newest successful yhikas-admin sync is older than the staleness window.',\n schedule: resolved.watchdogSchedule,\n })\n\n registerJob(watchdogJob, async () => {\n const { runWatchdogJob } = await import('./jobs.js')\n // Throwing IS the alert: the queue's alerter fires only from\n // `failJob`'s dead-letter branch, so this is how a check that\n // detects ABSENCE of success reaches a channel that only knows how\n // to report presence of an error.\n await runWatchdogJob(app, resolved)\n })\n },\n },\n\n shared: {\n // The widget's words and the room facts source's label and provenance.\n messages: yhikasSyncMessages,\n },\n\n // Only for a site that keeps its rooms as pages — nothing to show otherwise.\n ...(resolved.roomPages !== undefined\n ? {\n adminUi: {\n dashboardWidgets: [{ id: 'yhikas-room-pages', component: RoomPagesWidget, order: 10 }],\n },\n }\n : {}),\n })\n}\n\nexport type {\n ResolvedRoomPagesConfig,\n ResolvedYhikasSyncConfig,\n RoomPageBlock,\n RoomPageDraft,\n YhikasRoomPagesConfig,\n YhikasSyncConfig,\n} from './config.js'\nexport { isRoomOffer } from './room-offers.js'\nexport type { RoomTypeForPage } from './room-type-view.js'\n"],"mappings":"+SAkCA,MAAM,EACJ,EAWF,eAAe,EAAgB,EAA2D,CAExF,OAAO,MADe,OAAO,oCAAA,CACd,gBAAgB,CAAK,CACtC,CAkBA,SAAgB,EAAW,EAA2B,CACpD,IAAM,EAAW,EAAwB,CAAM,EAE/C,OAAO,EAAa,CAClB,KAAM,EA0BN,SAAU,CAAC,uBAAwB,oBAAoB,EAEvD,OAAQ,CACN,OAAQ,EAER,SAAU,CAAC,GAAG,EAAe,CAAQ,CAAC,EAMtC,OAAQ,CACN,kBAAmB,EAAqB,MAIxC,kBAAmB,EAAqB,KAC1C,EAOA,KAAM,KAAO,IAAoB,CAC/B,GAAI,CAAC,EAAS,QAAS,CACrB,EAAI,OAAO,KACT,CAAE,OAAQ,CAAwB,EAClC,kFACF,EACA,MACF,CAEA,GAAM,CAAE,YAAW,eAAgB,MAAM,OAAO,6BAShD,EAPgB,EAAU,CACxB,KAAM,EACN,YACE,wFACF,SAAU,EAAS,QACrB,CAEkB,EAAG,SAAY,CAC/B,GAAM,CAAE,cAAe,MAAM,OAAO,uBAMpC,MAAM,EAAW,EAAK,CAAQ,CAChC,CAAC,EASD,EAPoB,EAAU,CAC5B,KAAM,EACN,YACE,yFACF,SAAU,EAAS,gBACrB,CAEsB,EAAG,SAAY,CACnC,GAAM,CAAE,kBAAmB,MAAM,OAAO,uBAKxC,MAAM,EAAe,EAAK,CAAQ,CACpC,CAAC,CACH,CACF,EAEA,OAAQ,CAEN,SAAU,CACZ,EAGA,GAAI,EAAS,YAAc,IAAA,GAMvB,CAAC,EALD,CACE,QAAS,CACP,iBAAkB,CAAC,CAAE,GAAI,oBAAqB,UAAW,EAAiB,MAAO,EAAG,CAAC,CACvF,CACF,CAEN,CAAC,CACH"}
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};export{t};
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./rolldown-runtime-DK3Fl9T5.mjs";import{n as t,r as n,t as r}from"./room-pages-table-Vd9BqTSl.mjs";var i=e({ROOM_PAGE_READ_CHUNK:()=>100,YhikasRoomPageRecordError:()=>s,assertRecordable:()=>l,boundedRefusal:()=>p,chunksOf:()=>u,createRoomPageRegistry:()=>g,leaseCutoff:()=>m});const a=100,o=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;var s=class extends Error{constructor(e){super(e),this.name=`YhikasRoomPageRecordError`}};function c(e){if(typeof e!=`string`||e.trim()===``||e.length>190)throw new s(`yhikas-sync: a room type code is 1–190 characters, got '${String(e)}'`)}function l(e,t){if(c(e),typeof t!=`string`||!o.test(t))throw new s(`yhikas-sync: a page id is a UUID, got '${String(t)}'`)}function u(e,t){let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function d(e){return t.includes(e)?e:`created`}function f(e){return e===null?null:n.includes(e)?e:`refused`}function p(e){let t=e.replace(/\s+/g,` `).trim();return t.length<=500?t:`${t.slice(0,499)}…`}function m(e){return new Date(e.getTime()-r)}const h=new Date(0);function g(e){return{async states(t){let n=new Map;for(let r of u([...new Set(t)],100)){let t=await e.findMany({where:{code:{in:r}},limit:r.length});for(let e of t)n.set(e.code,{pageId:e.pageId,claimToken:e.claimToken,lastErrorAt:e.lastErrorAt})}return n},async claim(t,n,r){return c(t),(await e.tryClaim({code:t,pageId:null,origin:`created`,claimToken:n,claimedAt:r,createdAt:r},{target:`code`,set:{claimToken:n,claimedAt:r,origin:`created`},replaceWhen:{pageId:null,claimedAt:{lt:m(r)}}})).acquired},async complete(t,n,r,i){return l(t,r),await e.update({code:t,claimToken:n,pageId:null},{pageId:r,claimToken:null,claimedAt:null,lastError:null,lastErrorKind:null,lastErrorAt:null,createdAt:i})!==null},async refuse(t,n,r,i){c(t),await e.update({code:t,claimToken:n,pageId:null},{claimToken:null,claimedAt:h,lastError:p(r.message),lastErrorKind:r.kind,lastErrorAt:i})},async record(t,n,r,i){return l(t,n),(await e.tryClaim({code:t,pageId:n,origin:r,claimToken:null,claimedAt:null,createdAt:i},{target:`code`,set:{pageId:n,origin:r,claimToken:null,claimedAt:null,lastError:null,lastErrorKind:null,lastErrorAt:null},replaceWhen:{pageId:null,$or:[{claimedAt:null},{claimedAt:{lt:m(i)}}]}})).acquired},async list(t){return(await e.findMany({orderBy:[{column:`createdAt`,dir:`desc`}],limit:t})).map(e=>({code:e.code,pageId:e.pageId,origin:d(e.origin),createdAt:e.createdAt,lastError:e.lastError,lastErrorKind:f(e.lastErrorKind),lastErrorAt:e.lastErrorAt}))}}}export{g as a,u as i,s as n,i as o,l as r,a as t};
2
+ //# sourceMappingURL=room-page-registry-BFbyGTC7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-page-registry-BFbyGTC7.mjs","names":["known"],"sources":["../src/room-page-registry.ts"],"sourcesContent":["/**\n * Reading and writing `yhikas_room_pages` — which room type codes have had\n * their page, which are claimed by a run creating one, and why the last\n * attempt for a code was refused.\n *\n * Behind an interface, like `sync-state.ts`, so the draft creation and the\n * dashboard widget are exercisable against an in-memory fake — and the real\n * store's statements are pinned by `room-page-registry.test.ts`.\n *\n * ## The claim protocol\n *\n * 1. `claim(code, token)` — one `INSERT … ON CONFLICT (code) DO UPDATE …\n * WHERE page_id IS NULL AND claimed_at < lease-cutoff` (`tryClaim`). It wins\n * for a new code and for a LAPSED claim; it loses to a row that has its\n * page and to a fresh claim held by another run. Postgres serialises the\n * statement, so two runs can never both win.\n * 2. Only the winner creates the page, then `complete(code, token, pageId)` —\n * which only writes a row still claimed by `token` with no page.\n * 3. A refusal is `refuse(code, token, …)`: the reason is kept on the row and\n * the claim is released at once, so the next run retries.\n * 4. A crash between 1 and 2 leaves a claim with no page; it lapses after\n * {@link ROOM_PAGE_CLAIM_LEASE_MS}, and the next run re-checks by slug\n * BEFORE claiming — so a page the crashed run did create is recorded as\n * found, never created twice.\n */\n\nimport {\n MAX_REFUSAL_LENGTH,\n ROOM_PAGE_CLAIM_LEASE_MS,\n ROOM_PAGE_ORIGINS,\n ROOM_PAGE_REFUSAL_KINDS,\n type RoomPageOrigin,\n type RoomPageRefusalKind,\n type yhikasRoomPagesTable,\n} from './room-pages-table.js'\n\nexport interface RoomPageRecord {\n readonly code: string\n /** `null` while the code is only claimed (or its last attempt was refused). */\n readonly pageId: string | null\n readonly origin: RoomPageOrigin\n readonly createdAt: Date\n readonly lastError: string | null\n readonly lastErrorKind: RoomPageRefusalKind | null\n readonly lastErrorAt: Date | null\n}\n\n/** What a run needs to know about a code before acting on it. */\nexport interface RoomPageState {\n /** Set once the code has its page — final. */\n readonly pageId: string | null\n /** A run's claim, still on the row (that run is creating the page, or crashed doing so). */\n readonly claimToken: string | null\n /** When the last attempt was refused — drives the back-off and the ordering. */\n readonly lastErrorAt: Date | null\n}\n\nexport interface RoomPageRefusal {\n readonly kind: RoomPageRefusalKind\n readonly message: string\n}\n\nexport interface RoomPageRegistry {\n /** The rows of `codes` that exist, by code. Read in chunks — nothing dropped. */\n states(codes: readonly string[]): Promise<ReadonlyMap<string, RoomPageState>>\n /**\n * Atomically claim `code` for creating its page. `true` only for the one\n * run that may create it: a new code, or a lapsed claim with no page.\n */\n claim(code: string, token: string, now: Date): Promise<boolean>\n /** Give a claim held by `token` its page. `false` when the claim is no longer this run's. */\n complete(code: string, token: string, pageId: string, now: Date): Promise<boolean>\n /** Keep why a claimed code got no page, and release the claim so the next run retries. */\n refuse(code: string, token: string, refusal: RoomPageRefusal, now: Date): Promise<void>\n /**\n * Record a page that already exists (found by slug, or written by a site's\n * migration). Never moves a page already recorded, and never overrides a\n * FRESH claim of another run. `true` when this call recorded it.\n */\n record(code: string, pageId: string, origin: RoomPageOrigin, now: Date): Promise<boolean>\n /** The newest rows first, at most `limit`. */\n list(limit: number): Promise<readonly RoomPageRecord[]>\n}\n\ntype RoomPagesClient = ReturnType<typeof yhikasRoomPagesTable.makeClient>\n\n/** Codes per `IN (…)` read — the whole input is read, in chunks of this size. */\nexport const ROOM_PAGE_READ_CHUNK = 100\n\n/** UUID, any version — `pageId` is a uuid column, and Postgres rejects anything else. */\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\n/** Thrown for a code or page id that cannot be recorded. */\nexport class YhikasRoomPageRecordError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'YhikasRoomPageRecordError'\n }\n}\n\nfunction assertCode(code: string): void {\n if (typeof code !== 'string' || code.trim() === '' || code.length > 190) {\n throw new YhikasRoomPageRecordError(\n `yhikas-sync: a room type code is 1–190 characters, got '${String(code)}'`,\n )\n }\n}\n\n/** Refuse what the table cannot hold, with a message naming the value. */\nexport function assertRecordable(code: string, pageId: string): void {\n assertCode(code)\n if (typeof pageId !== 'string' || !UUID_PATTERN.test(pageId)) {\n throw new YhikasRoomPageRecordError(`yhikas-sync: a page id is a UUID, got '${String(pageId)}'`)\n }\n}\n\n/** `values` split into chunks of `size`. */\nexport function chunksOf<T>(values: readonly T[], size: number): T[][] {\n const out: T[][] = []\n for (let i = 0; i < values.length; i += size) out.push(values.slice(i, i + size))\n return out\n}\n\nfunction originOf(value: string): RoomPageOrigin {\n const known: readonly string[] = ROOM_PAGE_ORIGINS\n return known.includes(value) ? (value as RoomPageOrigin) : 'created'\n}\n\nfunction refusalKindOf(value: string | null): RoomPageRefusalKind | null {\n if (value === null) return null\n const known: readonly string[] = ROOM_PAGE_REFUSAL_KINDS\n return known.includes(value) ? (value as RoomPageRefusalKind) : 'refused'\n}\n\n/** A stored message: one line, bounded. */\nexport function boundedRefusal(message: string): string {\n const line = message.replace(/\\s+/g, ' ').trim()\n return line.length <= MAX_REFUSAL_LENGTH ? line : `${line.slice(0, MAX_REFUSAL_LENGTH - 1)}…`\n}\n\n/** The moment before which a claim without a page has lapsed. */\nexport function leaseCutoff(now: Date): Date {\n return new Date(now.getTime() - ROOM_PAGE_CLAIM_LEASE_MS)\n}\n\n/** The epoch — a released claim, older than every lease. */\nconst RELEASED = new Date(0)\n\nexport function createRoomPageRegistry(client: RoomPagesClient): RoomPageRegistry {\n return {\n async states(codes) {\n const out = new Map<string, RoomPageState>()\n for (const chunk of chunksOf([...new Set(codes)], ROOM_PAGE_READ_CHUNK)) {\n // `code` is the primary key: at most one row per code, so the limit is exact.\n const rows = await client.findMany({ where: { code: { in: chunk } }, limit: chunk.length })\n for (const row of rows) {\n out.set(row.code, {\n pageId: row.pageId,\n claimToken: row.claimToken,\n lastErrorAt: row.lastErrorAt,\n })\n }\n }\n return out\n },\n\n async claim(code, token, now) {\n assertCode(code)\n const result = await client.tryClaim(\n {\n code,\n pageId: null,\n origin: 'created',\n claimToken: token,\n claimedAt: now,\n createdAt: now,\n },\n {\n target: 'code',\n set: { claimToken: token, claimedAt: now, origin: 'created' },\n // A row WITH a page is final; a fresh claim belongs to another run.\n replaceWhen: { pageId: null, claimedAt: { lt: leaseCutoff(now) } },\n },\n )\n return result.acquired\n },\n\n async complete(code, token, pageId, now) {\n assertRecordable(code, pageId)\n const row = await client.update(\n { code, claimToken: token, pageId: null },\n {\n pageId,\n claimToken: null,\n claimedAt: null,\n lastError: null,\n lastErrorKind: null,\n lastErrorAt: null,\n createdAt: now,\n },\n )\n return row !== null\n },\n\n async refuse(code, token, refusal, now) {\n assertCode(code)\n await client.update(\n { code, claimToken: token, pageId: null },\n {\n claimToken: null,\n // Released at once — the next run retries rather than waiting out the lease.\n claimedAt: RELEASED,\n lastError: boundedRefusal(refusal.message),\n lastErrorKind: refusal.kind,\n lastErrorAt: now,\n },\n )\n },\n\n async record(code, pageId, origin, now) {\n assertRecordable(code, pageId)\n const result = await client.tryClaim(\n {\n code,\n pageId,\n origin,\n claimToken: null,\n claimedAt: null,\n createdAt: now,\n },\n {\n target: 'code',\n set: {\n pageId,\n origin,\n claimToken: null,\n claimedAt: null,\n lastError: null,\n lastErrorKind: null,\n lastErrorAt: null,\n },\n // Only over a row with no page whose claim (if any) has lapsed.\n replaceWhen: {\n pageId: null,\n $or: [{ claimedAt: null }, { claimedAt: { lt: leaseCutoff(now) } }],\n },\n },\n )\n return result.acquired\n },\n\n async list(limit) {\n const rows = await client.findMany({\n orderBy: [{ column: 'createdAt', dir: 'desc' }],\n limit,\n })\n return rows.map((row) => ({\n code: row.code,\n pageId: row.pageId,\n origin: originOf(row.origin),\n createdAt: row.createdAt,\n lastError: row.lastError,\n lastErrorKind: refusalKindOf(row.lastErrorKind),\n lastErrorAt: row.lastErrorAt,\n }))\n },\n }\n}\n"],"mappings":"wSAuFA,MAAa,EAAuB,IAG9B,EAAe,kEAGrB,IAAa,EAAb,cAA+C,KAAM,CACnD,YAAY,EAAiB,CAC3B,MAAM,CAAO,EACb,KAAK,KAAO,2BACd,CACF,EAEA,SAAS,EAAW,EAAoB,CACtC,GAAI,OAAO,GAAS,UAAY,EAAK,KAAK,IAAM,IAAM,EAAK,OAAS,IAClE,MAAM,IAAI,EACR,2DAA2D,OAAO,CAAI,EAAE,EAC1E,CAEJ,CAGA,SAAgB,EAAiB,EAAc,EAAsB,CAEnE,GADA,EAAW,CAAI,EACX,OAAO,GAAW,UAAY,CAAC,EAAa,KAAK,CAAM,EACzD,MAAM,IAAI,EAA0B,0CAA0C,OAAO,CAAM,EAAE,EAAE,CAEnG,CAGA,SAAgB,EAAY,EAAsB,EAAqB,CACrE,IAAM,EAAa,CAAC,EACpB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EAAM,EAAI,KAAK,EAAO,MAAM,EAAG,EAAI,CAAI,CAAC,EAChF,OAAO,CACT,CAEA,SAAS,EAAS,EAA+B,CAE/C,OAAOA,EAAM,SAAS,CAAK,EAAK,EAA2B,SAC7D,CAEA,SAAS,EAAc,EAAkD,CAGvE,OAFI,IAAU,KAAa,KAEpBA,EAAM,SAAS,CAAK,EAAK,EAAgC,SAClE,CAGA,SAAgB,EAAe,EAAyB,CACtD,IAAM,EAAO,EAAQ,QAAQ,OAAQ,GAAG,CAAC,CAAC,KAAK,EAC/C,OAAO,EAAK,QAAA,IAA+B,EAAO,GAAG,EAAK,MAAM,EAAA,GAAyB,EAAE,EAC7F,CAGA,SAAgB,EAAY,EAAiB,CAC3C,OAAO,IAAI,KAAK,EAAI,QAAQ,EAAI,CAAwB,CAC1D,CAGA,MAAM,EAAW,IAAI,KAAK,CAAC,EAE3B,SAAgB,EAAuB,EAA2C,CAChF,MAAO,CACL,MAAM,OAAO,EAAO,CAClB,IAAM,EAAM,IAAI,IAChB,IAAK,IAAM,KAAS,EAAS,CAAC,GAAG,IAAI,IAAI,CAAK,CAAC,EAAA,GAAuB,EAAG,CAEvE,IAAM,EAAO,MAAM,EAAO,SAAS,CAAE,MAAO,CAAE,KAAM,CAAE,GAAI,CAAM,CAAE,EAAG,MAAO,EAAM,MAAO,CAAC,EAC1F,IAAK,IAAM,KAAO,EAChB,EAAI,IAAI,EAAI,KAAM,CAChB,OAAQ,EAAI,OACZ,WAAY,EAAI,WAChB,YAAa,EAAI,WACnB,CAAC,CAEL,CACA,OAAO,CACT,EAEA,MAAM,MAAM,EAAM,EAAO,EAAK,CAkB5B,OAjBA,EAAW,CAAI,GAiBR,MAhBc,EAAO,SAC1B,CACE,OACA,OAAQ,KACR,OAAQ,UACR,WAAY,EACZ,UAAW,EACX,UAAW,CACb,EACA,CACE,OAAQ,OACR,IAAK,CAAE,WAAY,EAAO,UAAW,EAAK,OAAQ,SAAU,EAE5D,YAAa,CAAE,OAAQ,KAAM,UAAW,CAAE,GAAI,EAAY,CAAG,CAAE,CAAE,CACnE,CACF,EAAA,CACc,QAChB,EAEA,MAAM,SAAS,EAAM,EAAO,EAAQ,EAAK,CAcvC,OAbA,EAAiB,EAAM,CAAM,EAatB,MAZW,EAAO,OACvB,CAAE,OAAM,WAAY,EAAO,OAAQ,IAAK,EACxC,CACE,SACA,WAAY,KACZ,UAAW,KACX,UAAW,KACX,cAAe,KACf,YAAa,KACb,UAAW,CACb,CACF,IACe,IACjB,EAEA,MAAM,OAAO,EAAM,EAAO,EAAS,EAAK,CACtC,EAAW,CAAI,EACf,MAAM,EAAO,OACX,CAAE,OAAM,WAAY,EAAO,OAAQ,IAAK,EACxC,CACE,WAAY,KAEZ,UAAW,EACX,UAAW,EAAe,EAAQ,OAAO,EACzC,cAAe,EAAQ,KACvB,YAAa,CACf,CACF,CACF,EAEA,MAAM,OAAO,EAAM,EAAQ,EAAQ,EAAK,CA6BtC,OA5BA,EAAiB,EAAM,CAAM,GA4BtB,MA3Bc,EAAO,SAC1B,CACE,OACA,SACA,SACA,WAAY,KACZ,UAAW,KACX,UAAW,CACb,EACA,CACE,OAAQ,OACR,IAAK,CACH,SACA,SACA,WAAY,KACZ,UAAW,KACX,UAAW,KACX,cAAe,KACf,YAAa,IACf,EAEA,YAAa,CACX,OAAQ,KACR,IAAK,CAAC,CAAE,UAAW,IAAK,EAAG,CAAE,UAAW,CAAE,GAAI,EAAY,CAAG,CAAE,CAAE,CAAC,CACpE,CACF,CACF,EAAA,CACc,QAChB,EAEA,MAAM,KAAK,EAAO,CAKhB,OAAO,MAJY,EAAO,SAAS,CACjC,QAAS,CAAC,CAAE,OAAQ,YAAa,IAAK,MAAO,CAAC,EAC9C,OACF,CAAC,EAAA,CACW,IAAK,IAAS,CACxB,KAAM,EAAI,KACV,OAAQ,EAAI,OACZ,OAAQ,EAAS,EAAI,MAAM,EAC3B,UAAW,EAAI,UACf,UAAW,EAAI,UACf,cAAe,EAAc,EAAI,aAAa,EAC9C,YAAa,EAAI,WACnB,EAAE,CACJ,CACF,CACF"}