@kernhq/module-quire 0.11.1 → 0.13.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 (52) hide show
  1. package/dist/contract/models.d.ts +46 -0
  2. package/dist/contract/models.d.ts.map +1 -1
  3. package/dist/contract/models.js +73 -0
  4. package/dist/contract/models.js.map +1 -1
  5. package/dist/contract/permissions.d.ts +17 -1
  6. package/dist/contract/permissions.d.ts.map +1 -1
  7. package/dist/contract/permissions.js +35 -0
  8. package/dist/contract/permissions.js.map +1 -1
  9. package/dist/contract/router.d.ts +756 -0
  10. package/dist/contract/router.d.ts.map +1 -1
  11. package/dist/contract/router.js +346 -2
  12. package/dist/contract/router.js.map +1 -1
  13. package/dist/server/_impl.d.ts +826 -0
  14. package/dist/server/_impl.d.ts.map +1 -1
  15. package/dist/server/_impl.js +336 -2
  16. package/dist/server/_impl.js.map +1 -1
  17. package/dist/server/schema.d.ts +318 -1
  18. package/dist/server/schema.d.ts.map +1 -1
  19. package/dist/server/schema.js +86 -0
  20. package/dist/server/schema.js.map +1 -1
  21. package/dist/server/services/access.d.ts +1 -0
  22. package/dist/server/services/access.d.ts.map +1 -1
  23. package/dist/server/services/index.d.ts +3 -0
  24. package/dist/server/services/index.d.ts.map +1 -1
  25. package/dist/server/services/index.js +5 -1
  26. package/dist/server/services/index.js.map +1 -1
  27. package/dist/server/services/pages.d.ts.map +1 -1
  28. package/dist/server/services/pages.js +6 -0
  29. package/dist/server/services/pages.js.map +1 -1
  30. package/dist/server/services/publications.d.ts +191 -0
  31. package/dist/server/services/publications.d.ts.map +1 -0
  32. package/dist/server/services/publications.js +772 -0
  33. package/dist/server/services/publications.js.map +1 -0
  34. package/dist/server/services/versions.d.ts +26 -1
  35. package/dist/server/services/versions.d.ts.map +1 -1
  36. package/dist/server/services/versions.js +44 -15
  37. package/dist/server/services/versions.js.map +1 -1
  38. package/migrations/0008_publications.sql +140 -0
  39. package/migrations/0009_public_asset_references.sql +35 -0
  40. package/migrations/meta/_journal.json +14 -0
  41. package/package.json +1 -1
  42. package/src/client/components/PublishDialog.svelte +920 -0
  43. package/src/client/components/SidebarRecents.svelte +17 -1
  44. package/src/client/i18n.ts +448 -0
  45. package/src/client/index.ts +21 -0
  46. package/src/client/mock.ts +436 -2
  47. package/src/client/pages/PageView.svelte +139 -0
  48. package/src/client/public-url.ts +76 -0
  49. package/src/client/query.ts +16 -0
  50. package/src/contract/models.ts +77 -0
  51. package/src/contract/permissions.ts +54 -1
  52. package/src/contract/router.ts +380 -1
@@ -1 +1 @@
1
- {"version":3,"file":"_impl.d.ts","sourceRoot":"","sources":["../../src/server/_impl.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAElB,KAAK,MAAM,EACX,cAAc,EACd,KAAK,cAAc,EAIpB,MAAM,gBAAgB,CAAA;AASvB;;;;;;;GAOG;AACH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,CAAA;AAI3D,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAm8BxC"}
1
+ {"version":3,"file":"_impl.d.ts","sourceRoot":"","sources":["../../src/server/_impl.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAElB,KAAK,MAAM,EAEX,cAAc,EACd,KAAK,cAAc,EAIpB,MAAM,gBAAgB,CAAA;AAWvB;;;;;;;GAOG;AACH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,CAAA;AAuE3D,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2uCxC"}
@@ -1,10 +1,12 @@
1
- import { defineModule, defineServerModule, KernError, packageVersion, requires, workspaceScoped, } from '@kernhq/kernel';
1
+ import { ANONYMOUS } from '@kernhq/contracts';
2
+ import { defineModule, defineServerModule, KernError, o, packageVersion, requires, workspaceScoped, } from '@kernhq/kernel';
2
3
  import { implement } from '@orpc/server';
3
4
  import { MODULE_ID, quireContract, quireEvents } from '../contract/index.js';
4
5
  import { toComment } from './services/comments.js';
5
6
  import { quireServices } from './services/index.js';
6
7
  import { createNotify } from './services/notify.js';
7
8
  import { documentNameOf, toPage } from './services/pages.js';
9
+ import { toPublication } from './services/publications.js';
8
10
  import { toVersion } from './services/versions.js';
9
11
  /**
10
12
  * The router, kept apart from `index.ts` so `module.test.ts` can walk it without booting a kernel.
@@ -16,10 +18,91 @@ import { toVersion } from './services/versions.js';
16
18
  */
17
19
  export { defineModule, defineServerModule, packageVersion };
18
20
  const os = implement(quireContract).$context();
21
+ /**
22
+ * The anonymous counterpart of `workspaceScoped`, for the `public.*` surface and nothing else.
23
+ *
24
+ * `workspaceScoped` rejects an anonymous principal outright, which is exactly right for every other
25
+ * procedure in this module and impossible for these six. What is still worth keeping from it is the
26
+ * module switch: a workspace that has turned Quire off must not keep serving its published sites,
27
+ * and the answer is **404 rather than `MODULE_DISABLED`**, because a signed-out stranger asking for
28
+ * a URL is owed "there is nothing here" and not "there is something here that this customer has
29
+ * switched off". The one thing it must not do is `requireMember`.
30
+ */
31
+ const UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
32
+ const publicSurface = o.middleware(async ({ context, next }, input) => {
33
+ const { workspaceId } = input;
34
+ /*
35
+ * A middleware runs *before* the contract validates its input, so this is the first and only
36
+ * thing that sees the workspace segment — and it used to hand whatever arrived to
37
+ * `kernel.isModuleEnabled`, whose own schema is `z.uuid()`. So `/api/quire/public/not-a-uuid/x`
38
+ * came back as a ZodError this module has no case for: **HTTP 500** on the one surface that
39
+ * promises everything unresolvable is a 404, and a stack trace written at error level for every
40
+ * such request, on an endpoint anyone on the internet can reach as fast as they like.
41
+ *
42
+ * The shape is checked here instead. Anything that is not a workspace id is the same "there is
43
+ * nothing here" as a workspace that has switched Quire off — which is the right answer, because a
44
+ * signed-out stranger is owed no more than that. `.catch` covers the rest for the same reason: a
45
+ * settings lookup that fails must not become a 500 either.
46
+ */
47
+ if (typeof workspaceId !== 'string' || !UUID.test(workspaceId))
48
+ throw new KernError('NOT_FOUND', 'There is no published site at this address');
49
+ const enabled = await context.kernel.isModuleEnabled(workspaceId, MODULE_ID).catch(() => false);
50
+ if (!enabled)
51
+ throw new KernError('NOT_FOUND', 'There is no published site at this address');
52
+ return next();
53
+ });
54
+ /**
55
+ * Serve a public procedure to its author exactly as it is served to a stranger.
56
+ *
57
+ * The principal is replaced, not merely ignored. A signed-in author opening their own published URL
58
+ * is the *only* person who ever checks whether a public site works, so a handler that could quietly
59
+ * consult `context.principal` would show them a site nobody else can see — and the failure is
60
+ * invisible in exactly the situation where somebody is looking for it. With the principal gone
61
+ * there is nothing to consult: `userId` is null, there are no memberships, and any permission
62
+ * question a future handler asked would be answered for a stranger.
63
+ *
64
+ * `authz.int.test.ts` asserts the consequence rather than the mechanism — every `check: 'public'`
65
+ * procedure must answer a denied administrator and an anonymous request identically.
66
+ */
67
+ const anonymousOnly = o.middleware(({ context, next }) => next({ context: { ...context, principal: ANONYMOUS } }));
68
+ /**
69
+ * The one refusal every unservable publication shares, in one place so it cannot drift.
70
+ *
71
+ * It drifted. A publication whose root page had since been trashed was 404 from `site` and `page`,
72
+ * **200 with an empty body** from `search` and `sitemap`, and `indexable: true` from `robots` — so
73
+ * four procedures written to be indistinguishable from "no such slug" were, between them, an
74
+ * existence oracle for a fifth state nobody had enumerated. And `page` said "page" where the others
75
+ * said "site", which separated a locked publication from a missing one on the endpoint least able
76
+ * to afford it.
77
+ *
78
+ * The rule the wording now follows: **a refusal about the publication says "site", and a refusal
79
+ * about a path inside a servable one says "page".** A reader who mistypes a page of a real handbook
80
+ * is the only person who ever sees the second sentence.
81
+ */
82
+ const noSite = () => new KernError('NOT_FOUND', 'There is no published site at this address');
19
83
  export function implement_(kernel) {
20
84
  const scoped = os.use(workspaceScoped(MODULE_ID));
85
+ const open = os.use(publicSurface).use(anonymousOnly);
21
86
  const svc = quireServices(kernel);
22
87
  const notify = createNotify(kernel);
88
+ /**
89
+ * A publication that can actually be served, and its whole tree, or `noSite`.
90
+ *
91
+ * Every anonymous read but `site` and `robots` starts here, so the four ways of not being
92
+ * servable — no such slug, expired, still locked, nothing left under the root — are one answer
93
+ * written once rather than four handlers agreeing by hand. `site` needs its own shape because
94
+ * `locked` is a state it reports rather than refuses, and `robots` because it never refuses at
95
+ * all.
96
+ */
97
+ const servable = async (tx, workspaceId, slug, token) => {
98
+ const pub = await svc.publications.bySlug(tx, workspaceId, slug);
99
+ if (!(await svc.publications.unlocked(pub, token)))
100
+ throw noSite();
101
+ const nodes = await svc.publications.tree(tx, workspaceId, pub);
102
+ if (!nodes[0])
103
+ throw noSite();
104
+ return { pub, nodes };
105
+ };
23
106
  const run = (context, workspaceId, fn) => kernel.database.withWorkspace(workspaceId, fn, { userId: context.principal.userId });
24
107
  /**
25
108
  * "May you do this to *that* page", answered through the page's ancestor chain.
@@ -666,7 +749,18 @@ export function implement_(kernel) {
666
749
  const row = await run(context, input.workspaceId, async (tx) => {
667
750
  const scope = await svc.access.scopeOf(tx, input.workspaceId, input.pageId);
668
751
  await svc.access.requirePage(context.principal, 'quire.page.publish', input.workspaceId, scope);
669
- return svc.versions.publish(tx, context.principal, input.workspaceId, input.pageId, input.label);
752
+ const updated = await svc.versions.publish(tx, context.principal, input.workspaceId, input.pageId, input.label);
753
+ /*
754
+ * Draw the version now, while there is a principal and a writable transaction.
755
+ *
756
+ * A version is immutable, so every rendering of it is identical — and the public path
757
+ * is read-only and has nobody to sign a picture on behalf of. Doing it here is what
758
+ * makes an anonymous read a single indexed row read instead of a CRDT decode, and it is
759
+ * why `page_versions.html` exists at all.
760
+ */
761
+ if (updated.publishedVersionId)
762
+ await svc.publications.renderVersion(tx, input.workspaceId, updated.publishedVersionId);
763
+ return updated;
670
764
  });
671
765
  await kernel.emit(quireEvents.pagePublished, {
672
766
  pageId: input.pageId,
@@ -689,6 +783,246 @@ export function implement_(kernel) {
689
783
  return toPage(row);
690
784
  }),
691
785
  },
786
+ /**
787
+ * Publishing to the internet, from the inside.
788
+ *
789
+ * Every one of these asks `quire.page.publish` about the publication's **root page**, because
790
+ * the root is the page whose whole subtree is being handed out. A publication id carries no
791
+ * scope of its own, so the row is read first and the question is asked about what it points at
792
+ * — the same shape as a `databases.*` procedure resolving its host page.
793
+ */
794
+ publications: {
795
+ list: scoped.publications.list.use(requires('quire.page.publish')).handler(({ input, context }) => run(context, input.workspaceId, async (tx) => {
796
+ await svc.access.spaceRow(tx, input.workspaceId, input.spaceId);
797
+ await svc.access.requireSpace(context.principal, 'quire.page.publish', input.workspaceId, input.spaceId);
798
+ /*
799
+ * Filtered as well as gated, for the reason `pages.trash` is: the procedure is reached by
800
+ * being allowed to publish *somewhere* in the space, and a publication names a root page
801
+ * — so an unfiltered row would hand its slug and its root's existence to somebody a
802
+ * page-scoped DENY has closed that page to.
803
+ */
804
+ const rows = await svc.publications.list(tx, input.workspaceId, input.spaceId);
805
+ const kept = [];
806
+ for (const row of rows) {
807
+ const scope = await svc.access.scopeOf(tx, input.workspaceId, row.rootPageId).catch(() => null);
808
+ if (!scope)
809
+ continue;
810
+ if (await svc.access.canPage(context.principal, 'quire.page.publish', input.workspaceId, scope))
811
+ kept.push(toPublication(row));
812
+ }
813
+ return kept;
814
+ })),
815
+ get: scoped.publications.get.use(requires('quire.page.publish')).handler(({ input, context }) => run(context, input.workspaceId, async (tx) => {
816
+ const row = await svc.publications.row(tx, input.workspaceId, input.publicationId);
817
+ await requirePage(tx, context, input.workspaceId, row.rootPageId, 'quire.page.publish');
818
+ return toPublication(row);
819
+ })),
820
+ create: scoped.publications.create
821
+ .use(requires('quire.page.publish'))
822
+ .handler(async ({ input, context }) => {
823
+ const row = await run(context, input.workspaceId, async (tx) => {
824
+ await requirePage(tx, context, input.workspaceId, input.rootPageId, 'quire.page.publish');
825
+ const created = await svc.publications.create(tx, context.principal, input.workspaceId, input);
826
+ // Only ever does anything for pages published before this feature existed; a publish
827
+ // since then rendered its own HTML. Bounded, and a shortfall is a log line rather than a
828
+ // failed request — see `backfill`.
829
+ await svc.publications.backfill(tx, input.workspaceId, created);
830
+ return created;
831
+ });
832
+ await announce(input.workspaceId, 'publication', row.id, 'created');
833
+ return toPublication(row);
834
+ }),
835
+ update: scoped.publications.update
836
+ .use(requires('quire.page.publish'))
837
+ .handler(async ({ input, context }) => {
838
+ const { workspaceId, publicationId, ...patch } = input;
839
+ const row = await run(context, workspaceId, async (tx) => {
840
+ const existing = await svc.publications.row(tx, workspaceId, publicationId);
841
+ await requirePage(tx, context, workspaceId, existing.rootPageId, 'quire.page.publish');
842
+ const updated = await svc.publications.update(tx, workspaceId, publicationId, patch);
843
+ await svc.publications.backfill(tx, workspaceId, updated);
844
+ return updated;
845
+ });
846
+ await announce(workspaceId, 'publication', row.id, 'updated');
847
+ return toPublication(row);
848
+ }),
849
+ remove: scoped.publications.remove
850
+ .use(requires('quire.page.publish'))
851
+ .handler(async ({ input, context }) => {
852
+ await run(context, input.workspaceId, async (tx) => {
853
+ const existing = await svc.publications.row(tx, input.workspaceId, input.publicationId);
854
+ await requirePage(tx, context, input.workspaceId, existing.rootPageId, 'quire.page.publish');
855
+ await svc.publications.remove(tx, input.workspaceId, input.publicationId);
856
+ });
857
+ await announce(input.workspaceId, 'publication', input.publicationId, 'deleted');
858
+ return { ok: true };
859
+ }),
860
+ optOut: scoped.publications.optOut
861
+ .use(requires('quire.page.publish'))
862
+ .handler(async ({ input, context }) => {
863
+ const excluded = await run(context, input.workspaceId, async (tx) => {
864
+ await requirePage(tx, context, input.workspaceId, input.pageId, 'quire.page.publish');
865
+ return svc.publications.setExcluded(tx, input.workspaceId, input.pageId, input.excluded);
866
+ });
867
+ await announce(input.workspaceId, 'page', input.pageId, 'updated');
868
+ return { pageId: input.pageId, excluded };
869
+ }),
870
+ },
871
+ /**
872
+ * The signed-out surface.
873
+ *
874
+ * Every handler here has the same three lines at the top and they are the whole security model:
875
+ * resolve the **publication** from the slug (which fails closed on a slug nobody has taken and
876
+ * on one that has expired), check the door, and then walk the tree *from the publication's
877
+ * root*. Nothing is looked up by an id a caller supplied, because nothing here takes one — so
878
+ * "guessing a sibling id" is not a request this API can express.
879
+ *
880
+ * `open` is `publicSurface` + `anonymousOnly`: no membership check, no permission check, and no
881
+ * principal to accidentally consult. Reads run in `svc.publications.read`, which is a read-only
882
+ * transaction, so a write that slipped in here fails in Postgres rather than in a review.
883
+ */
884
+ public: {
885
+ site: open.public.site.handler(({ input }) => svc.publications.read(input.workspaceId, async (tx) => {
886
+ const pub = await svc.publications.bySlug(tx, input.workspaceId, input.slug);
887
+ const theme = pub.theme;
888
+ if (!(await svc.publications.unlocked(pub, input.token)))
889
+ return { slug: pub.slug, theme, locked: true, site: null };
890
+ const nodes = await svc.publications.tree(tx, input.workspaceId, pub);
891
+ // No root means the root page is archived, trashed, opted out, unpublished or not a page.
892
+ // The publication row survives that; the site does not.
893
+ const root = nodes[0];
894
+ if (!root)
895
+ throw noSite();
896
+ const newest = nodes.reduce((at, node) => Math.max(at, node.published_at.getTime()), root.published_at.getTime());
897
+ return {
898
+ slug: pub.slug,
899
+ theme,
900
+ locked: false,
901
+ site: {
902
+ title: pub.seoTitle || root.title || 'Untitled',
903
+ description: pub.seoDescription,
904
+ ogImageUrl: pub.ogImageUrl,
905
+ indexable: pub.indexable,
906
+ updatedAt: new Date(newest).toISOString(),
907
+ nav: nodes.map((node) => ({
908
+ path: node.path,
909
+ parentPath: node.parentPath,
910
+ title: node.title || 'Untitled',
911
+ icon: node.icon,
912
+ })),
913
+ },
914
+ };
915
+ })),
916
+ page: open.public.page.handler(({ input }) => svc.publications.read(input.workspaceId, async (tx) => {
917
+ const { nodes } = await servable(tx, input.workspaceId, input.slug, input.token);
918
+ const node = svc.publications.find(nodes, input.path);
919
+ return {
920
+ path: node.path,
921
+ title: node.title || 'Untitled',
922
+ icon: node.icon,
923
+ coverUrl: node.cover_url,
924
+ html: await svc.publications.html(tx, input.workspaceId, node, nodes, input.basePath),
925
+ /*
926
+ * The version's timestamp, not the page's. `pages.updated_at` moves every time somebody
927
+ * types in the draft, so publishing it would tell the internet when an unpublished
928
+ * change was being worked on.
929
+ */
930
+ publishedAt: node.published_at.toISOString(),
931
+ etag: svc.publications.etagFor(node.version_id),
932
+ breadcrumbs: trailTo(nodes, node),
933
+ };
934
+ })),
935
+ search: open.public.search.handler(({ input }) => svc.publications.read(input.workspaceId, async (tx) => {
936
+ const { nodes } = await servable(tx, input.workspaceId, input.slug, input.token);
937
+ const hits = await svc.publications.search(tx, input.workspaceId, nodes, input.q, input.limit);
938
+ return {
939
+ items: hits.map((hit) => ({
940
+ path: hit.node.path,
941
+ title: hit.node.title || 'Untitled',
942
+ snippet: hit.snippet,
943
+ })),
944
+ };
945
+ })),
946
+ sitemap: open.public.sitemap.handler(({ input }) => svc.publications.read(input.workspaceId, async (tx) => {
947
+ const pub = await svc.publications.bySlug(tx, input.workspaceId, input.slug);
948
+ // A sitemap exists for crawlers, so a site nobody is meant to crawl has an empty one
949
+ // rather than a private one. Answering with the tree here would put every path of a
950
+ // password-protected handbook in a file whose whole purpose is to be fetched by robots.
951
+ if (pub.passwordHash || !pub.indexable)
952
+ return { entries: [] };
953
+ const nodes = await svc.publications.tree(tx, input.workspaceId, pub);
954
+ // A publication whose root has since been trashed serves nothing, so it is the same 404
955
+ // as a slug nobody took — an empty sitemap here would say "this address is a site" to a
956
+ // crawler that has just been told the opposite by every other procedure.
957
+ if (!nodes[0])
958
+ throw noSite();
959
+ return {
960
+ entries: nodes.map((node) => ({
961
+ path: node.path,
962
+ lastModified: node.published_at.toISOString(),
963
+ })),
964
+ };
965
+ })),
966
+ robots: open.public.robots.handler(({ input }) => svc.publications.read(input.workspaceId, async (tx) => {
967
+ /*
968
+ * The one place a missing publication is not an error: a crawler asking about a slug
969
+ * nobody has taken, one that has expired, and one behind a password have to get the same
970
+ * answer, or this becomes the oracle every other procedure refuses to be.
971
+ *
972
+ * There is a **fourth** state and this comment used to list three. A publication whose
973
+ * root page has since been trashed, archived, opted out or unpublished still has its row:
974
+ * `site`, `page`, `search` and `sitemap` all answer 404 for it, and robots answered
975
+ * `indexable: true` with a sitemap path — so the one procedure written to never
976
+ * distinguish anything was the only one admitting that the slug existed. It is the walk
977
+ * rather than the row that decides, which is why the tree is read here.
978
+ */
979
+ const pub = await svc.publications.bySlug(tx, input.workspaceId, input.slug).catch(() => null);
980
+ if (!pub || pub.passwordHash || !pub.indexable)
981
+ return { indexable: false, sitemapPath: null };
982
+ const nodes = await svc.publications.tree(tx, input.workspaceId, pub);
983
+ if (!nodes[0])
984
+ return { indexable: false, sitemapPath: null };
985
+ // Relative to whatever prefix the route layer serves this site under; the module has no
986
+ // way to know that, and guessing would put a wrong absolute URL in a robots file.
987
+ return { indexable: true, sitemapPath: 'sitemap.xml' };
988
+ })),
989
+ /**
990
+ * The bytes of one picture on a published page.
991
+ *
992
+ * It goes through `servable` like every other read, so a picture is behind the same door, the
993
+ * same expiry and the same walk as the prose it sits in — and `svc.publications.asset` then
994
+ * insists the file is referenced by a version *in this tree* before it fetches anything. That
995
+ * second check is what stops a reference lifted out of one published site from resolving
996
+ * against another in the same workspace.
997
+ */
998
+ asset: open.public.asset.handler(({ input }) => svc.publications.read(input.workspaceId, async (tx) => {
999
+ const { nodes } = await servable(tx, input.workspaceId, input.slug, input.token);
1000
+ return svc.publications.asset(tx, input.workspaceId, nodes, input.asset);
1001
+ })),
1002
+ unlock: open.public.unlock.handler(({ input }) => svc.publications.read(input.workspaceId, async (tx) => {
1003
+ const pub = await svc.publications.bySlug(tx, input.workspaceId, input.slug);
1004
+ // A site with no password has no door to open, and saying so would confirm the slug
1005
+ // exists to somebody who has not been told anything else about it.
1006
+ if (!pub.passwordHash)
1007
+ throw new KernError('NOT_FOUND', 'There is no published site at this address');
1008
+ if (!(await svc.publications.checkPassword(pub, input.password)))
1009
+ throw new KernError('UNAUTHORIZED', 'That password does not open this site');
1010
+ return svc.publications.mintToken(pub);
1011
+ })),
1012
+ },
692
1013
  });
693
1014
  }
1015
+ /** The path from the front page down to this page's parent, for a breadcrumb trail. */
1016
+ function trailTo(nodes, node) {
1017
+ const byId = new Map(nodes.map((n) => [n.id, n]));
1018
+ const trail = [];
1019
+ const seen = new Set([node.id]);
1020
+ let at = node.parent_id === null ? null : (byId.get(node.parent_id) ?? null);
1021
+ while (at && !seen.has(at.id)) {
1022
+ seen.add(at.id);
1023
+ trail.push({ path: at.path, title: at.title || 'Untitled' });
1024
+ at = at.parent_id === null ? null : (byId.get(at.parent_id) ?? null);
1025
+ }
1026
+ return trail.reverse();
1027
+ }
694
1028
  //# sourceMappingURL=_impl.js.map