@dwk/microsub 0.1.0-beta.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 (83) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +92 -0
  3. package/dist/auth.d.ts +53 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +102 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/config.d.ts +102 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +64 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/consumer.d.ts +40 -0
  12. package/dist/consumer.d.ts.map +1 -0
  13. package/dist/consumer.js +87 -0
  14. package/dist/consumer.js.map +1 -0
  15. package/dist/discovery.d.ts +59 -0
  16. package/dist/discovery.d.ts.map +1 -0
  17. package/dist/discovery.js +190 -0
  18. package/dist/discovery.js.map +1 -0
  19. package/dist/fetch.d.ts +28 -0
  20. package/dist/fetch.d.ts.map +1 -0
  21. package/dist/fetch.js +72 -0
  22. package/dist/fetch.js.map +1 -0
  23. package/dist/handler.d.ts +24 -0
  24. package/dist/handler.d.ts.map +1 -0
  25. package/dist/handler.js +434 -0
  26. package/dist/handler.js.map +1 -0
  27. package/dist/hfeed.d.ts +25 -0
  28. package/dist/hfeed.d.ts.map +1 -0
  29. package/dist/hfeed.js +252 -0
  30. package/dist/hfeed.js.map +1 -0
  31. package/dist/index.d.ts +39 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +32 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/jf2.d.ts +69 -0
  36. package/dist/jf2.d.ts.map +1 -0
  37. package/dist/jf2.js +295 -0
  38. package/dist/jf2.js.map +1 -0
  39. package/dist/log.d.ts +44 -0
  40. package/dist/log.d.ts.map +1 -0
  41. package/dist/log.js +42 -0
  42. package/dist/log.js.map +1 -0
  43. package/dist/poll.d.ts +22 -0
  44. package/dist/poll.d.ts.map +1 -0
  45. package/dist/poll.js +39 -0
  46. package/dist/poll.js.map +1 -0
  47. package/dist/queue.d.ts +25 -0
  48. package/dist/queue.d.ts.map +1 -0
  49. package/dist/queue.js +13 -0
  50. package/dist/queue.js.map +1 -0
  51. package/dist/replay.d.ts +34 -0
  52. package/dist/replay.d.ts.map +1 -0
  53. package/dist/replay.js +49 -0
  54. package/dist/replay.js.map +1 -0
  55. package/dist/safe-fetch.d.ts +86 -0
  56. package/dist/safe-fetch.d.ts.map +1 -0
  57. package/dist/safe-fetch.js +311 -0
  58. package/dist/safe-fetch.js.map +1 -0
  59. package/dist/store.d.ts +131 -0
  60. package/dist/store.d.ts.map +1 -0
  61. package/dist/store.js +393 -0
  62. package/dist/store.js.map +1 -0
  63. package/dist/xml.d.ts +51 -0
  64. package/dist/xml.d.ts.map +1 -0
  65. package/dist/xml.js +196 -0
  66. package/dist/xml.js.map +1 -0
  67. package/package.json +49 -0
  68. package/src/auth.ts +184 -0
  69. package/src/config.ts +156 -0
  70. package/src/consumer.ts +140 -0
  71. package/src/discovery.ts +270 -0
  72. package/src/fetch.ts +82 -0
  73. package/src/handler.ts +594 -0
  74. package/src/hfeed.ts +287 -0
  75. package/src/index.ts +86 -0
  76. package/src/jf2.ts +394 -0
  77. package/src/log.ts +46 -0
  78. package/src/poll.ts +72 -0
  79. package/src/queue.ts +26 -0
  80. package/src/replay.ts +68 -0
  81. package/src/safe-fetch.ts +346 -0
  82. package/src/store.ts +644 -0
  83. package/src/xml.ts +229 -0
package/dist/hfeed.js ADDED
@@ -0,0 +1,252 @@
1
+ /**
2
+ * `@dwk/microsub` — `h-feed` / `h-entry` microformats parsing.
3
+ *
4
+ * When a followed source is an HTML page rather than a syndication feed, its
5
+ * entries are expressed as [microformats2](https://microformats.org/wiki/h-entry)
6
+ * `h-entry` items (usually inside an `h-feed`). This module extracts them into
7
+ * the same JF2 shape the other formats produce, using the Workers runtime's
8
+ * streaming `HTMLRewriter` rather than a regex or a bundled parser
9
+ * (`HTMLRewriter` is built into the runtime — zero script-size cost; see
10
+ * `spec/non-functional-requirements.md`). Because `HTMLRewriter` is a `workerd`
11
+ * global, this parser is async and exercised under the Workers test pool.
12
+ *
13
+ * It is a pragmatic extractor of the common `h-entry` properties — `u-url`,
14
+ * `p-name`, `e-content`, `dt-published`, `p-author` / nested `h-card`,
15
+ * `u-photo`, `p-category`, `u-in-reply-to`, `u-like-of` — not a full mf2 engine.
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ function classes(value) {
20
+ return value === null ? [] : value.trim().split(/\s+/).filter(Boolean);
21
+ }
22
+ /**
23
+ * HTML void elements have no end tag, so `HTMLRewriter#onEndTag` throws on them.
24
+ * They also carry no text content — a `u-photo` / `dt-published` on one always
25
+ * takes its value from an attribute — so we commit immediately and never
26
+ * register an end-tag handler for them.
27
+ */
28
+ const VOID_ELEMENTS = new Set([
29
+ "area",
30
+ "base",
31
+ "br",
32
+ "col",
33
+ "embed",
34
+ "hr",
35
+ "img",
36
+ "input",
37
+ "link",
38
+ "meta",
39
+ "param",
40
+ "source",
41
+ "track",
42
+ "wbr",
43
+ ]);
44
+ function absolute(href, base) {
45
+ try {
46
+ return new URL(href, base).toString();
47
+ }
48
+ catch {
49
+ return href;
50
+ }
51
+ }
52
+ /** FNV-1a hash (base36), matching {@link ./jf2}'s fallback id derivation. */
53
+ function hash(input) {
54
+ let h = 0x811c9dc5;
55
+ for (let i = 0; i < input.length; i++) {
56
+ h ^= input.charCodeAt(i);
57
+ h = Math.imul(h, 0x01000193);
58
+ }
59
+ return (h >>> 0).toString(36);
60
+ }
61
+ /** The first `(p|u|e|dt)-name` microformats class on an element, if any. */
62
+ function propertyClass(classList) {
63
+ for (const cls of classList) {
64
+ const match = /^(p|u|e|dt)-(.+)$/.exec(cls);
65
+ if (match) {
66
+ return { name: match[2], format: match[1] };
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ function commitEntry(entry, baseUrl) {
72
+ const url = entry.url ? absolute(entry.url, baseUrl) : undefined;
73
+ const content = entry.content
74
+ ? { text: entry.content }
75
+ : undefined;
76
+ const author = entry.author
77
+ ? (() => {
78
+ const card = {
79
+ type: "card",
80
+ ...(entry.author.name ? { name: entry.author.name } : {}),
81
+ ...(entry.author.url
82
+ ? { url: absolute(entry.author.url, baseUrl) }
83
+ : {}),
84
+ ...(entry.author.photo
85
+ ? { photo: absolute(entry.author.photo, baseUrl) }
86
+ : {}),
87
+ };
88
+ return card.name || card.url || card.photo ? card : undefined;
89
+ })()
90
+ : undefined;
91
+ const id = url ?? hash(`${entry.name ?? ""}${entry.content ?? ""}`);
92
+ const out = { type: "entry", _id: id };
93
+ if (url)
94
+ out.url = url;
95
+ if (entry.published)
96
+ out.published = entry.published;
97
+ if (entry.name)
98
+ out.name = entry.name;
99
+ if (content)
100
+ out.content = content;
101
+ if (author)
102
+ out.author = author;
103
+ if (entry.category.length > 0)
104
+ out.category = entry.category;
105
+ if (entry.photo.length > 0)
106
+ out.photo = entry.photo.map((p) => absolute(p, baseUrl));
107
+ if (entry.inReplyTo)
108
+ out["in-reply-to"] = absolute(entry.inReplyTo, baseUrl);
109
+ if (entry.likeOf)
110
+ out["like-of"] = absolute(entry.likeOf, baseUrl);
111
+ return out;
112
+ }
113
+ /**
114
+ * Extract JF2 entries from an HTML document's `h-entry` microformats. Entries
115
+ * are returned in document order. Returns `[]` when the document has none.
116
+ */
117
+ export async function parseHFeed(html, baseUrl) {
118
+ if (html === "")
119
+ return [];
120
+ const entries = [];
121
+ const entryStack = [];
122
+ const cardStack = [];
123
+ const propStack = [];
124
+ const top = (arr) => arr[arr.length - 1];
125
+ const rewriter = new HTMLRewriter().on("*", {
126
+ element(el) {
127
+ const classList = classes(el.getAttribute("class"));
128
+ const isVoid = VOID_ELEMENTS.has(el.tagName);
129
+ if (classList.includes("h-entry") && !isVoid) {
130
+ const entry = { photo: [], category: [] };
131
+ entryStack.push(entry);
132
+ el.onEndTag(() => {
133
+ const finished = entryStack.pop();
134
+ if (finished)
135
+ entries.push(commitEntry(finished, baseUrl));
136
+ });
137
+ return;
138
+ }
139
+ if (classList.includes("h-card") && !isVoid) {
140
+ const card = {};
141
+ const entry = top(entryStack);
142
+ // A bare p-author h-card attaches as the current entry's author.
143
+ if (entry && entry.author === undefined)
144
+ entry.author = card;
145
+ cardStack.push(card);
146
+ el.onEndTag(() => {
147
+ cardStack.pop();
148
+ });
149
+ return;
150
+ }
151
+ const prop = propertyClass(classList);
152
+ if (prop === null)
153
+ return;
154
+ const target = top(cardStack) ?? top(entryStack);
155
+ if (target === undefined)
156
+ return;
157
+ const frame = {
158
+ name: prop.name,
159
+ format: prop.format,
160
+ target,
161
+ buf: "",
162
+ };
163
+ // u-* and dt-* take their value from an attribute when present; that value
164
+ // is committed immediately, so no text capture (or end tag) is needed.
165
+ if (prop.format === "u") {
166
+ const attr = el.getAttribute("href") ??
167
+ el.getAttribute("src") ??
168
+ el.getAttribute("data") ??
169
+ el.getAttribute("poster");
170
+ if (attr !== null) {
171
+ assignProperty(frame, attr);
172
+ return;
173
+ }
174
+ }
175
+ else if (prop.format === "dt") {
176
+ const attr = el.getAttribute("datetime") ?? el.getAttribute("value");
177
+ if (attr !== null) {
178
+ assignProperty(frame, attr);
179
+ return;
180
+ }
181
+ }
182
+ // Text-valued property: a void element has no text to capture.
183
+ if (isVoid)
184
+ return;
185
+ propStack.push(frame);
186
+ el.onEndTag(() => {
187
+ const finished = propStack.pop();
188
+ if (finished)
189
+ assignProperty(finished, finished.buf.trim());
190
+ });
191
+ },
192
+ text(chunk) {
193
+ const frame = top(propStack);
194
+ if (frame)
195
+ frame.buf += chunk.text;
196
+ },
197
+ });
198
+ await rewriter.transform(new Response(html)).text();
199
+ return entries;
200
+ }
201
+ /** Write a resolved property value onto its target entry or card. */
202
+ function assignProperty(frame, value) {
203
+ if (value === "")
204
+ return;
205
+ const target = frame.target;
206
+ // Card target (author).
207
+ if (isCard(target)) {
208
+ if (frame.name === "name" && target.name === undefined)
209
+ target.name = value;
210
+ else if (frame.name === "url" && target.url === undefined)
211
+ target.url = value;
212
+ else if (frame.name === "photo" && target.photo === undefined)
213
+ target.photo = value;
214
+ return;
215
+ }
216
+ // Entry target.
217
+ switch (frame.name) {
218
+ case "url":
219
+ target.url ??= value;
220
+ break;
221
+ case "name":
222
+ target.name ??= value;
223
+ break;
224
+ case "content":
225
+ target.content ??= value;
226
+ break;
227
+ case "summary":
228
+ target.content ??= value;
229
+ break;
230
+ case "published":
231
+ target.published ??= value;
232
+ break;
233
+ case "photo":
234
+ target.photo.push(value);
235
+ break;
236
+ case "category":
237
+ target.category.push(value);
238
+ break;
239
+ case "in-reply-to":
240
+ target.inReplyTo ??= value;
241
+ break;
242
+ case "like-of":
243
+ target.likeOf ??= value;
244
+ break;
245
+ default:
246
+ break;
247
+ }
248
+ }
249
+ function isCard(target) {
250
+ return !("photo" in target && Array.isArray(target.photo));
251
+ }
252
+ //# sourceMappingURL=hfeed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hfeed.js","sourceRoot":"","sources":["../src/hfeed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA+BH,SAAS,OAAO,CAAC,KAAoB;IACnC,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,MAAM;IACN,MAAM;IACN,IAAI;IACJ,KAAK;IACL,OAAO;IACP,IAAI;IACJ,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,QAAQ;IACR,OAAO;IACP,KAAK;CACN,CAAC,CAAC;AAEH,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAY;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,SAAS,IAAI,CAAC,KAAa;IACzB,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,4EAA4E;AAC5E,SAAS,aAAa,CACpB,SAAmB;IAEnB,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAW,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAe,EAAE,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,WAAW,CAAC,KAAmB,EAAE,OAAe;IACvD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,MAAM,OAAO,GAA2B,KAAK,CAAC,OAAO;QACnD,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE;QACzB,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,MAAM,GAA0B,KAAK,CAAC,MAAM;QAChD,CAAC,CAAC,CAAC,GAAG,EAAE;YACJ,MAAM,IAAI,GAAc;gBACtB,IAAI,EAAE,MAAM;gBACZ,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzD,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;oBAClB,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE;oBAC9C,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK;oBACpB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE;oBAClD,CAAC,CAAC,EAAE,CAAC;aACR,CAAC;YACF,OAAO,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAChE,CAAC,CAAC,EAAE;QACN,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC;IAEpE,MAAM,GAAG,GAA4B,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;IAChE,IAAI,GAAG;QAAE,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC;IACvB,IAAI,KAAK,CAAC,SAAS;QAAE,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IACrD,IAAI,KAAK,CAAC,IAAI;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACtC,IAAI,OAAO;QAAE,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;IACnC,IAAI,MAAM;QAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IAChC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC7D,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACxB,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,IAAI,KAAK,CAAC,SAAS;QAAE,GAAG,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC7E,IAAI,KAAK,CAAC,MAAM;QAAE,GAAG,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnE,OAAO,GAA0B,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAY,EACZ,OAAe;IAEf,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAE3B,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAmB,EAAE,CAAC;IACtC,MAAM,SAAS,GAAkB,EAAE,CAAC;IACpC,MAAM,SAAS,GAAgB,EAAE,CAAC;IAElC,MAAM,GAAG,GAAG,CAAI,GAAQ,EAAiB,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEhE,MAAM,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE;QAC1C,OAAO,CAAC,EAAE;YACR,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;YAE7C,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC7C,MAAM,KAAK,GAAiB,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;gBACxD,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACvB,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE;oBACf,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;oBAClC,IAAI,QAAQ;wBAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC7D,CAAC,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC5C,MAAM,IAAI,GAAgB,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC9B,iEAAiE;gBACjE,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;oBAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;gBAC7D,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACrB,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE;oBACf,SAAS,CAAC,GAAG,EAAE,CAAC;gBAClB,CAAC,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YACtC,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO;YAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;YACjD,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO;YAEjC,MAAM,KAAK,GAAc;gBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM;gBACN,GAAG,EAAE,EAAE;aACR,CAAC;YAEF,2EAA2E;YAC3E,uEAAuE;YACvE,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxB,MAAM,IAAI,GACR,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;oBACvB,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC;oBACtB,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;oBACvB,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAC5B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAClB,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;oBAC5B,OAAO;gBACT,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBACrE,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAClB,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;oBAC5B,OAAO;gBACT,CAAC;YACH,CAAC;YAED,+DAA+D;YAC/D,IAAI,MAAM;gBAAE,OAAO;YAEnB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE;gBACf,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC;gBACjC,IAAI,QAAQ;oBAAE,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,KAAK;YACR,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,KAAK;gBAAE,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC;QACrC,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,qEAAqE;AACrE,SAAS,cAAc,CAAC,KAAgB,EAAE,KAAa;IACrD,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO;IACzB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,wBAAwB;IACxB,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACnB,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC;aACvE,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS;YACvD,MAAM,CAAC,GAAG,GAAG,KAAK,CAAC;aAChB,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;YAC3D,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QACvB,OAAO;IACT,CAAC;IACD,gBAAgB;IAChB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,KAAK;YACR,MAAM,CAAC,GAAG,KAAK,KAAK,CAAC;YACrB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC;YACtB,MAAM;QACR,KAAK,SAAS;YACZ,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC;YACzB,MAAM;QACR,KAAK,SAAS;YACZ,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC;YACzB,MAAM;QACR,KAAK,WAAW;YACd,MAAM,CAAC,SAAS,KAAK,KAAK,CAAC;YAC3B,MAAM;QACR,KAAK,OAAO;YACV,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,MAAM;QACR,KAAK,UAAU;YACb,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5B,MAAM;QACR,KAAK,aAAa;YAChB,MAAM,CAAC,SAAS,KAAK,KAAK,CAAC;YAC3B,MAAM;QACR,KAAK,SAAS;YACZ,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC;YACxB,MAAM;QACR;YACE,MAAM;IACV,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,MAAkC;IAChD,OAAO,CAAC,CAAC,OAAO,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAE,MAAuB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `@dwk/microsub` — a [Microsub](https://indieweb.org/Microsub-spec) server: the
3
+ * IndieWeb **read side**.
4
+ *
5
+ * Endpoint package: exports a factory returning a `fetch`-compatible handler,
6
+ * mountable under a path prefix so it composes with other `@dwk` packages in one
7
+ * Worker. It manages feed **subscriptions** organised into **channels**, polls
8
+ * and parses sources server-side (Atom / RSS / JSON Feed / `h-feed`), and serves
9
+ * a normalised **JF2** timeline to reader clients (Monocle, Together,
10
+ * Indigenous) — keeping the user's reading state on infrastructure they own.
11
+ *
12
+ * It consumes the DPoP-bound IndieAuth access tokens issued by `@dwk/indieauth`
13
+ * (same authorization as `@dwk/micropub`), so a token minted for a different
14
+ * `me` cannot read here. Subscriptions, timeline, and read-state live in D1 — a
15
+ * strongly-consistent store, never KV. Polling runs off the read path on a
16
+ * Cron-triggered queue; every outbound fetch is SSRF-guarded.
17
+ *
18
+ * @see spec/packages/microsub.md
19
+ * @packageDocumentation
20
+ */
21
+ export { createMicrosub } from "./handler";
22
+ export type { MicrosubHandler } from "./handler";
23
+ export { createMicrosubPoller } from "./poll";
24
+ export type { MicrosubScheduledHandler } from "./poll";
25
+ export { createMicrosubQueueConsumer } from "./consumer";
26
+ export type { MicrosubQueueConsumer, ConsumerOptions } from "./consumer";
27
+ export { resolveConfig } from "./config";
28
+ export type { MicrosubConfig, MicrosubEnv, ResolvedConfig } from "./config";
29
+ export { createMicrosubStore, NOTIFICATIONS_CHANNEL, type MicrosubStore, type MicrosubStoreEnv, type ChannelRecord, type FollowRecord, type StoredItem, type ItemPage, type ListOptions, type FeedCache, } from "./store";
30
+ export { authorize, tokenFromHeader, hasScope, type AuthEnv, type AuthResult, type AuthSuccess, type AuthFailure, } from "./auth";
31
+ export { parseFeed, type Jf2Entry, type Jf2Author, type Jf2Content, } from "./jf2";
32
+ export { parseHFeed } from "./hfeed";
33
+ export { discoverFeed, fetchFeed, type DiscoveredFeed, type FetchedFeed, type DiscoveryOptions, } from "./discovery";
34
+ export { safeFetch, assertPublicUrl, isPrivateOrReservedHost, SsrfError, type SsrfReason, } from "./safe-fetch";
35
+ export type { FetchLike } from "./fetch";
36
+ export type { MicrosubJob, PollJob } from "./queue";
37
+ export { MicrosubLogEvent } from "./log";
38
+ export type { Logger, Metrics } from "@dwk/log";
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC9C,YAAY,EAAE,wBAAwB,EAAE,MAAM,QAAQ,CAAC;AAEvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,SAAS,GACf,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,SAAS,EACT,eAAe,EACf,QAAQ,EACR,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,SAAS,EACT,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EACL,YAAY,EACZ,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,SAAS,EACT,eAAe,EACf,uBAAuB,EACvB,SAAS,EACT,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACzC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `@dwk/microsub` — a [Microsub](https://indieweb.org/Microsub-spec) server: the
3
+ * IndieWeb **read side**.
4
+ *
5
+ * Endpoint package: exports a factory returning a `fetch`-compatible handler,
6
+ * mountable under a path prefix so it composes with other `@dwk` packages in one
7
+ * Worker. It manages feed **subscriptions** organised into **channels**, polls
8
+ * and parses sources server-side (Atom / RSS / JSON Feed / `h-feed`), and serves
9
+ * a normalised **JF2** timeline to reader clients (Monocle, Together,
10
+ * Indigenous) — keeping the user's reading state on infrastructure they own.
11
+ *
12
+ * It consumes the DPoP-bound IndieAuth access tokens issued by `@dwk/indieauth`
13
+ * (same authorization as `@dwk/micropub`), so a token minted for a different
14
+ * `me` cannot read here. Subscriptions, timeline, and read-state live in D1 — a
15
+ * strongly-consistent store, never KV. Polling runs off the read path on a
16
+ * Cron-triggered queue; every outbound fetch is SSRF-guarded.
17
+ *
18
+ * @see spec/packages/microsub.md
19
+ * @packageDocumentation
20
+ */
21
+ export { createMicrosub } from "./handler";
22
+ export { createMicrosubPoller } from "./poll";
23
+ export { createMicrosubQueueConsumer } from "./consumer";
24
+ export { resolveConfig } from "./config";
25
+ export { createMicrosubStore, NOTIFICATIONS_CHANNEL, } from "./store";
26
+ export { authorize, tokenFromHeader, hasScope, } from "./auth";
27
+ export { parseFeed, } from "./jf2";
28
+ export { parseHFeed } from "./hfeed";
29
+ export { discoverFeed, fetchFeed, } from "./discovery";
30
+ export { safeFetch, assertPublicUrl, isPrivateOrReservedHost, SsrfError, } from "./safe-fetch";
31
+ export { MicrosubLogEvent } from "./log";
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAGzD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GAStB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,SAAS,EACT,eAAe,EACf,QAAQ,GAKT,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,SAAS,GAIV,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EACL,YAAY,EACZ,SAAS,GAIV,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,SAAS,EACT,eAAe,EACf,uBAAuB,EACvB,SAAS,GAEV,MAAM,cAAc,CAAC;AAKtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC"}
package/dist/jf2.d.ts ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * `@dwk/microsub` — feed → JF2 normalisation.
3
+ *
4
+ * Microsub serves a single normalised timeline regardless of a source's wire
5
+ * format. This module turns the four feed formats a reader meets — JSON Feed,
6
+ * Atom, RSS 2.0, and `h-feed` microformats (the last parsed upstream in
7
+ * {@link ./hfeed}) — into [JF2](https://jf2.spec.indieweb.org/) `entry` objects.
8
+ *
9
+ * Everything here is **pure**: a feed body (and the format-sniffing on its
10
+ * content type) in, an array of JF2 entries out, each carrying a stable `_id`
11
+ * derived from the source's own identifier so the store can dedupe across polls.
12
+ * The XML formats go through the dependency-free reader in {@link ./xml}.
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+ /** A JF2 author card. */
17
+ export interface Jf2Author {
18
+ readonly type: "card";
19
+ readonly name?: string;
20
+ readonly url?: string;
21
+ readonly photo?: string;
22
+ }
23
+ /** Structured JF2 content (`html` and/or its plain-text rendering). */
24
+ export interface Jf2Content {
25
+ readonly html?: string;
26
+ readonly text?: string;
27
+ }
28
+ /** A normalised JF2 timeline entry. */
29
+ export interface Jf2Entry {
30
+ readonly type: "entry";
31
+ /**
32
+ * Stable per-entry identifier, derived from the source's own id/guid/url. The
33
+ * store keys timeline rows on this so re-polling a feed does not duplicate
34
+ * entries.
35
+ */
36
+ readonly _id: string;
37
+ readonly url?: string;
38
+ readonly published?: string;
39
+ readonly name?: string;
40
+ readonly summary?: string;
41
+ readonly content?: Jf2Content;
42
+ readonly author?: Jf2Author;
43
+ readonly category?: readonly string[];
44
+ readonly photo?: readonly string[];
45
+ readonly "in-reply-to"?: string;
46
+ readonly "like-of"?: string;
47
+ /** Read flag, attached by the store on read (never by the parser). */
48
+ readonly _is_read?: boolean;
49
+ }
50
+ /**
51
+ * Order a feed's entries oldest-first for insertion, so the newest entry is
52
+ * appended last and receives the highest paging `seq`.
53
+ *
54
+ * When the entries carry dates they are sorted chronologically (a feed's own
55
+ * order is not trusted). When none do — some `h-feed` pages — the feed is
56
+ * assumed newest-first and simply reversed. The sort is stable, so undated
57
+ * entries keep their relative position among dated ones.
58
+ */
59
+ export declare function orderEntriesForInsert(entries: readonly Jf2Entry[]): Jf2Entry[];
60
+ /**
61
+ * Parse a fetched feed body into JF2 entries, sniffing the format from the
62
+ * content type and the document root. Returns `[]` for an unrecognised or
63
+ * unparseable body (the caller treats an empty parse as "nothing new").
64
+ *
65
+ * `h-feed` HTML is **not** handled here — it needs the runtime's `HTMLRewriter`
66
+ * and lives in {@link ./hfeed}; this module is pure and runtime-free.
67
+ */
68
+ export declare function parseFeed(body: string, contentType: string, baseUrl: string): Jf2Entry[];
69
+ //# sourceMappingURL=jf2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jf2.d.ts","sourceRoot":"","sources":["../src/jf2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH,yBAAyB;AACzB,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,uCAAuC;AACvC,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAgRD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,QAAQ,EAAE,GAC3B,QAAQ,EAAE,CAYZ;AAcD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,QAAQ,EAAE,CAYZ"}