@rackbops/docket-types 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,21 @@
1
1
  # @rackbops/docket-types
2
2
 
3
3
  The tracker's task types, built on `@rackbops/docket-core`'s contract. Shipped so far:
4
- `reminder` (Lepid-Labs/city-hall#7): notify lane, `once` or `calendar`, done and snooze replies.
5
- The others land with their epics: renewal and price (#11), scout and wantlist (#13), research
6
- with the runner (E8).
4
+
5
+ - `reminder` (Lepid-Labs/city-hall#7): notify lane, `once` or `calendar`, done and snooze replies.
6
+ - `renewal` (city-hall#11): a `period` schedule rolling from the renewal date, asking keep /
7
+ cancel / renewed at lead time; the decision and the amount recorded per period (the current
8
+ row in the task's state, the history in the series); cancel ends the task.
9
+ - `price` (city-hall#11): a `poll` schedule; each run reads the page through the host's `Fetch`
10
+ port, extracts the price structurally (JSON-LD, product and Open Graph meta tags, a JSON body,
11
+ or the owner's pattern; `extractPrice`), appends it to the series and alerts once per crossing
12
+ of the drop line, measured from the first, last (default) or peak price seen; done stops it.
13
+
14
+ None of the three calls a model. The others land with their epics: scout and wantlist (#13),
15
+ research with the runner (E8).
7
16
 
8
17
  ```ts
9
- import { TASK_TYPES, reminder, TYPE_IDS, isTypeId } from "@rackbops/docket-types"
18
+ import { TASK_TYPES, reminder, renewal, price, TYPE_IDS, isTypeId } from "@rackbops/docket-types"
10
19
  ```
11
20
 
12
21
  `TASK_TYPES` is every shipped type keyed by id, ready for the dispatcher. Every type is declared
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Price extraction from a fetched page, structured sources first (city-hall#11): JSON-LD
3
+ * `Product` / `Offer` blocks, then the product and Open Graph price meta tags, then any JSON
4
+ * body, then a pattern the owner supplied. Pure string work on plain text, no DOM, no model;
5
+ * the model fallback the plan budgets for (section 6) is a later type, not this one.
6
+ */
7
+ export interface ExtractedPrice {
8
+ value: number;
9
+ currency?: string;
10
+ }
11
+ /**
12
+ * Reads a number out of a price string: "$1,299.99", "1.299,99 EUR", "49,90", "USD 20". The
13
+ * last separator followed by exactly two digits is the decimal mark; everything else is
14
+ * grouping. Null when there is no digit.
15
+ */
16
+ export declare function parsePrice(text: string): number | null;
17
+ /** The first price found walking a JSON value, offers before anything else at each level. */
18
+ export declare function priceFromJson(value: unknown, depth?: number): ExtractedPrice | null;
19
+ /** JSON-LD blocks in a page, parsed; broken blocks are skipped. */
20
+ export declare function jsonLdBlocks(html: string): unknown[];
21
+ /** The price from the page's meta tags, when a product or Open Graph price is declared. */
22
+ export declare function priceFromMeta(html: string): ExtractedPrice | null;
23
+ /** The first capture group of the owner's pattern, read as a price. */
24
+ export declare function priceFromPattern(body: string, pattern: string): ExtractedPrice | null;
25
+ /**
26
+ * The price a fetched body carries: the owner's pattern when given, else JSON-LD, else meta
27
+ * tags, else the body as JSON. Null when none of them yields a number.
28
+ */
29
+ export declare function extractPrice(body: string, pattern?: string): ExtractedPrice | null;
30
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBtD;AAsBD,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,cAAc,GAAG,IAAI,CAgC9E;AAID,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAUpD;AAgBD,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYjE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYrF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAiBlF"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Price extraction from a fetched page, structured sources first (city-hall#11): JSON-LD
3
+ * `Product` / `Offer` blocks, then the product and Open Graph price meta tags, then any JSON
4
+ * body, then a pattern the owner supplied. Pure string work on plain text, no DOM, no model;
5
+ * the model fallback the plan budgets for (section 6) is a later type, not this one.
6
+ */
7
+ /**
8
+ * Reads a number out of a price string: "$1,299.99", "1.299,99 EUR", "49,90", "USD 20". The
9
+ * last separator followed by exactly two digits is the decimal mark; everything else is
10
+ * grouping. Null when there is no digit.
11
+ */
12
+ export function parsePrice(text) {
13
+ const m = /-?\d[\d.,\s]*/.exec(text.replace(/ /g, " "));
14
+ if (!m)
15
+ return null;
16
+ let digits = m[0].replace(/\s+/g, "");
17
+ const negative = digits.startsWith("-");
18
+ if (negative)
19
+ digits = digits.slice(1);
20
+ const decimal = /[.,](\d{1,2})$/.exec(digits);
21
+ let whole = digits;
22
+ let fraction = "";
23
+ if (decimal) {
24
+ whole = digits.slice(0, decimal.index);
25
+ fraction = decimal[1] ?? "";
26
+ }
27
+ whole = whole.replace(/[.,]/g, "");
28
+ if (whole === "" && fraction === "")
29
+ return null;
30
+ const value = Number(`${whole || "0"}.${fraction || "0"}`);
31
+ if (!Number.isFinite(value))
32
+ return null;
33
+ return negative ? -value : value;
34
+ }
35
+ const CURRENCY = /^[A-Z]{3}$/;
36
+ function asCurrency(value) {
37
+ return typeof value === "string" && CURRENCY.test(value.trim().toUpperCase())
38
+ ? value.trim().toUpperCase()
39
+ : undefined;
40
+ }
41
+ function asPrice(value) {
42
+ if (typeof value === "number")
43
+ return Number.isFinite(value) ? value : null;
44
+ if (typeof value === "string")
45
+ return parsePrice(value);
46
+ return null;
47
+ }
48
+ function isRecord(value) {
49
+ return typeof value === "object" && value !== null && !Array.isArray(value);
50
+ }
51
+ /** The first price found walking a JSON value, offers before anything else at each level. */
52
+ export function priceFromJson(value, depth = 0) {
53
+ if (depth > 12)
54
+ return null;
55
+ if (Array.isArray(value)) {
56
+ for (const item of value) {
57
+ const found = priceFromJson(item, depth + 1);
58
+ if (found)
59
+ return found;
60
+ }
61
+ return null;
62
+ }
63
+ if (!isRecord(value))
64
+ return null;
65
+ for (const key of ["price", "lowPrice"]) {
66
+ const price = asPrice(value[key]);
67
+ if (price !== null) {
68
+ const currency = asCurrency(value.priceCurrency ?? value.currency);
69
+ return currency ? { value: price, currency } : { value: price };
70
+ }
71
+ }
72
+ const spec = value.priceSpecification;
73
+ if (spec) {
74
+ const found = priceFromJson(spec, depth + 1);
75
+ if (found)
76
+ return found;
77
+ }
78
+ if (value.offers) {
79
+ const found = priceFromJson(value.offers, depth + 1);
80
+ if (found)
81
+ return found;
82
+ }
83
+ for (const [key, child] of Object.entries(value)) {
84
+ if (key === "offers" || key === "priceSpecification")
85
+ continue;
86
+ const found = priceFromJson(child, depth + 1);
87
+ if (found)
88
+ return found;
89
+ }
90
+ return null;
91
+ }
92
+ const JSON_LD = /<script[^>]*type\s*=\s*["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
93
+ /** JSON-LD blocks in a page, parsed; broken blocks are skipped. */
94
+ export function jsonLdBlocks(html) {
95
+ const blocks = [];
96
+ for (const m of html.matchAll(JSON_LD)) {
97
+ try {
98
+ blocks.push(JSON.parse(m[1] ?? ""));
99
+ }
100
+ catch {
101
+ // not JSON after all; the next block may be
102
+ }
103
+ }
104
+ return blocks;
105
+ }
106
+ const META_KEYS = ["product:price:amount", "og:price:amount", "price"];
107
+ const META_CURRENCY_KEYS = ["product:price:currency", "og:price:currency", "priceCurrency"];
108
+ function metaContent(html, key) {
109
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
110
+ const tag = new RegExp(`<meta[^>]*(?:property|name|itemprop)\\s*=\\s*["']${escaped}["'][^>]*>`, "i").exec(html);
111
+ if (!tag)
112
+ return null;
113
+ const content = /content\s*=\s*["']([^"']*)["']/i.exec(tag[0]);
114
+ return content?.[1] ?? null;
115
+ }
116
+ /** The price from the page's meta tags, when a product or Open Graph price is declared. */
117
+ export function priceFromMeta(html) {
118
+ for (const key of META_KEYS) {
119
+ const raw = metaContent(html, key);
120
+ const value = raw === null ? null : parsePrice(raw);
121
+ if (value === null)
122
+ continue;
123
+ for (const ck of META_CURRENCY_KEYS) {
124
+ const currency = asCurrency(metaContent(html, ck));
125
+ if (currency)
126
+ return { value, currency };
127
+ }
128
+ return { value };
129
+ }
130
+ return null;
131
+ }
132
+ /** The first capture group of the owner's pattern, read as a price. */
133
+ export function priceFromPattern(body, pattern) {
134
+ let re;
135
+ try {
136
+ re = new RegExp(pattern, "i");
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ const m = re.exec(body);
142
+ const captured = m?.[1] ?? m?.[0];
143
+ if (captured === undefined)
144
+ return null;
145
+ const value = parsePrice(captured);
146
+ return value === null ? null : { value };
147
+ }
148
+ /**
149
+ * The price a fetched body carries: the owner's pattern when given, else JSON-LD, else meta
150
+ * tags, else the body as JSON. Null when none of them yields a number.
151
+ */
152
+ export function extractPrice(body, pattern) {
153
+ if (pattern)
154
+ return priceFromPattern(body, pattern);
155
+ for (const block of jsonLdBlocks(body)) {
156
+ const found = priceFromJson(block);
157
+ if (found)
158
+ return found;
159
+ }
160
+ const meta = priceFromMeta(body);
161
+ if (meta)
162
+ return meta;
163
+ const trimmed = body.trim();
164
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
165
+ try {
166
+ return priceFromJson(JSON.parse(trimmed));
167
+ }
168
+ catch {
169
+ return null;
170
+ }
171
+ }
172
+ return null;
173
+ }
174
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,12 +2,17 @@
2
2
  * @rackbops/docket-types -- the tracker's task types, built on @rackbops/docket-core.
3
3
  *
4
4
  * The six categories of the plan (section 1.2) as type identifiers, and the types shipped so far:
5
- * `reminder` with the reminder slice (Lepid-Labs/city-hall#7). The others land with their epics
6
- * (#11: renewal, price; #13: scout, wantlist; research with E8).
5
+ * `reminder` with the reminder slice (Lepid-Labs/city-hall#7), `renewal` and `price` with the
6
+ * renewal and price-tracker slice (city-hall#11). The others land with their epics (#13: scout,
7
+ * wantlist; research with E8).
7
8
  */
8
9
  import type { TaskType } from "@rackbops/docket-core";
9
10
  export type { Lane } from "@rackbops/docket-core";
11
+ export { type ExtractedPrice, extractPrice, jsonLdBlocks, parsePrice, priceFromJson, priceFromMeta, priceFromPattern, } from "./extract.js";
12
+ export { money } from "./money.js";
13
+ export { BASELINE_RULES, type BaselineRule, DEFAULT_BASELINE, DEFAULT_DROP_PERCENT, MISSES_BEFORE_TELLING, type PriceConfig, type PriceState, price, reference, } from "./price.js";
10
14
  export { DEFAULT_SNOOZE_MS, type ReminderConfig, reminder, snoozeUntil } from "./reminder.js";
15
+ export { decisionOf, isRenewalDecision, RENEWAL_DECISIONS, type RenewalConfig, type RenewalDecision, type RenewalState, renewal, } from "./renewal.js";
11
16
  /** The six task categories the tracker starts with (plan section 1.2), as type identifiers. */
12
17
  export declare const TYPE_IDS: readonly ["reminder", "renewal", "price", "research", "scout", "wantlist"];
13
18
  export type TypeId = (typeof TYPE_IDS)[number];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGrD,YAAY,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE7F,+FAA+F;AAC/F,eAAO,MAAM,QAAQ,YAAI,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,CAAU,CAAA;AAElG,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9C,2FAA2F;AAC3F,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,MAAM,CAEvD;AAED,2EAA2E;AAC3E,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAEjE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAKrD,YAAY,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EACL,KAAK,cAAc,EACnB,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,EACL,SAAS,GACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC7F,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,OAAO,GACR,MAAM,cAAc,CAAA;AAErB,+FAA+F;AAC/F,eAAO,MAAM,QAAQ,YAAI,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,CAAU,CAAA;AAElG,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9C,2FAA2F;AAC3F,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,MAAM,CAEvD;AAED,2EAA2E;AAC3E,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAIjE,CAAA"}
package/dist/index.js CHANGED
@@ -2,11 +2,18 @@
2
2
  * @rackbops/docket-types -- the tracker's task types, built on @rackbops/docket-core.
3
3
  *
4
4
  * The six categories of the plan (section 1.2) as type identifiers, and the types shipped so far:
5
- * `reminder` with the reminder slice (Lepid-Labs/city-hall#7). The others land with their epics
6
- * (#11: renewal, price; #13: scout, wantlist; research with E8).
5
+ * `reminder` with the reminder slice (Lepid-Labs/city-hall#7), `renewal` and `price` with the
6
+ * renewal and price-tracker slice (city-hall#11). The others land with their epics (#13: scout,
7
+ * wantlist; research with E8).
7
8
  */
9
+ import { price } from "./price.js";
8
10
  import { reminder } from "./reminder.js";
11
+ import { renewal } from "./renewal.js";
12
+ export { extractPrice, jsonLdBlocks, parsePrice, priceFromJson, priceFromMeta, priceFromPattern, } from "./extract.js";
13
+ export { money } from "./money.js";
14
+ export { BASELINE_RULES, DEFAULT_BASELINE, DEFAULT_DROP_PERCENT, MISSES_BEFORE_TELLING, price, reference, } from "./price.js";
9
15
  export { DEFAULT_SNOOZE_MS, reminder, snoozeUntil } from "./reminder.js";
16
+ export { decisionOf, isRenewalDecision, RENEWAL_DECISIONS, renewal, } from "./renewal.js";
10
17
  /** The six task categories the tracker starts with (plan section 1.2), as type identifiers. */
11
18
  export const TYPE_IDS = ["reminder", "renewal", "price", "research", "scout", "wantlist"];
12
19
  /** True when `value` names a task type. Narrows a string read from storage or the wire. */
@@ -16,5 +23,7 @@ export function isTypeId(value) {
16
23
  /** Every shipped type, keyed by id, ready for the dispatcher's `types`. */
17
24
  export const TASK_TYPES = Object.freeze({
18
25
  reminder: reminder,
26
+ renewal: renewal,
27
+ price: price,
19
28
  });
20
29
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAGxC,OAAO,EAAE,iBAAiB,EAAuB,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE7F,+FAA+F;AAC/F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,CAAU,CAAA;AAIlG,2FAA2F;AAC3F,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAQ,QAA8B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxD,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,UAAU,GAAgD,MAAM,CAAC,MAAM,CAAC;IACnF,QAAQ,EAAE,QAA6B;CACxC,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAGtC,OAAO,EAEL,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EACL,cAAc,EAEd,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EAGrB,KAAK,EACL,SAAS,GACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,iBAAiB,EAAuB,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC7F,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EAIjB,OAAO,GACR,MAAM,cAAc,CAAA;AAErB,+FAA+F;AAC/F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,CAAU,CAAA;AAIlG,2FAA2F;AAC3F,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAQ,QAA8B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxD,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,UAAU,GAAgD,MAAM,CAAC,MAAM,CAAC;IACnF,QAAQ,EAAE,QAA6B;IACvC,OAAO,EAAE,OAA4B;IACrC,KAAK,EAAE,KAA0B;CAClC,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ /** An amount with its unit, two decimals: `15.99 USD`; the unit may be empty. */
2
+ export declare function money(amount: number, currency: string): string;
3
+ //# sourceMappingURL=money.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D"}
package/dist/money.js ADDED
@@ -0,0 +1,5 @@
1
+ /** An amount with its unit, two decimals: `15.99 USD`; the unit may be empty. */
2
+ export function money(amount, currency) {
3
+ return `${amount.toFixed(2)} ${currency}`.trim();
4
+ }
5
+ //# sourceMappingURL=money.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,UAAU,KAAK,CAAC,MAAc,EAAE,QAAgB;IACpD,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAA;AAClD,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Category 3 (plan section 1.2, city-hall#11): track an item's price, record it, alert when it
3
+ * drops by 10% or more. A `poll` schedule; each run reads the page through the Fetch port,
4
+ * extracts the price (structured sources first, `extract.ts`), appends it to the task's series
5
+ * and compares it with the baseline the owner chose -- first seen, last seen or the peak --
6
+ * alerting once per crossing of the line. Notify lane, tier 0, no model.
7
+ */
8
+ export declare const BASELINE_RULES: readonly ["first", "last", "peak"];
9
+ export type BaselineRule = (typeof BASELINE_RULES)[number];
10
+ export declare const DEFAULT_DROP_PERCENT = 10;
11
+ export declare const DEFAULT_BASELINE: BaselineRule;
12
+ /** Consecutive polls without a price before the owner is told, once. */
13
+ export declare const MISSES_BEFORE_TELLING = 3;
14
+ export interface PriceConfig {
15
+ url: string;
16
+ /** Alert on a drop of at least this much, in percent of the baseline; default 10. */
17
+ dropPercent?: number;
18
+ /** What a drop is measured from; default `last`. */
19
+ baseline?: BaselineRule;
20
+ /** A regular expression whose first group is the price, for a page with no structured price. */
21
+ pattern?: string;
22
+ /** Shown with the price when the page declares no currency. */
23
+ currency?: string;
24
+ }
25
+ /** The current row: what has been seen, and whether the price sits under the alert line. */
26
+ export interface PriceState {
27
+ first: number | null;
28
+ last: number | null;
29
+ peak: number | null;
30
+ /** True while the price is at or below the line, so one crossing alerts once. */
31
+ below: boolean;
32
+ /** Consecutive polls that yielded no price. */
33
+ misses: number;
34
+ alerts: number;
35
+ }
36
+ /** The price a drop is measured from under `rule`, or null before anything was seen. */
37
+ export declare function reference(state: PriceState, rule: BaselineRule): number | null;
38
+ export declare const price: import("@rackbops/docket-core").TaskType<PriceConfig>;
39
+ //# sourceMappingURL=price.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price.d.ts","sourceRoot":"","sources":["../src/price.ts"],"names":[],"mappings":"AAUA;;;;;;GAMG;AAEH,eAAO,MAAM,cAAc,YAAI,OAAO,EAAE,MAAM,EAAE,MAAM,CAAU,CAAA;AAEhE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,eAAO,MAAM,oBAAoB,KAAK,CAAA;AACtC,eAAO,MAAM,gBAAgB,EAAE,YAAqB,CAAA;AACpD,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,gGAAgG;IAChG,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,iFAAiF;IACjF,KAAK,EAAE,OAAO,CAAA;IACd,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAmCD,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAS9E;AAgBD,eAAO,MAAM,KAAK,uDAmGhB,CAAA"}
package/dist/price.js ADDED
@@ -0,0 +1,172 @@
1
+ import { defineTaskType, } from "@rackbops/docket-core";
2
+ import { extractPrice } from "./extract.js";
3
+ import { money } from "./money.js";
4
+ /**
5
+ * Category 3 (plan section 1.2, city-hall#11): track an item's price, record it, alert when it
6
+ * drops by 10% or more. A `poll` schedule; each run reads the page through the Fetch port,
7
+ * extracts the price (structured sources first, `extract.ts`), appends it to the task's series
8
+ * and compares it with the baseline the owner chose -- first seen, last seen or the peak --
9
+ * alerting once per crossing of the line. Notify lane, tier 0, no model.
10
+ */
11
+ export const BASELINE_RULES = ["first", "last", "peak"];
12
+ export const DEFAULT_DROP_PERCENT = 10;
13
+ export const DEFAULT_BASELINE = "last";
14
+ /** Consecutive polls without a price before the owner is told, once. */
15
+ export const MISSES_BEFORE_TELLING = 3;
16
+ const FRESH = {
17
+ first: null,
18
+ last: null,
19
+ peak: null,
20
+ below: false,
21
+ misses: 0,
22
+ alerts: 0,
23
+ };
24
+ function stateOf(value) {
25
+ const s = value;
26
+ if (!s || typeof s !== "object" || typeof s.misses !== "number")
27
+ return { ...FRESH };
28
+ return {
29
+ first: s.first ?? null,
30
+ last: s.last ?? null,
31
+ peak: s.peak ?? null,
32
+ below: s.below ?? false,
33
+ misses: s.misses,
34
+ alerts: s.alerts ?? 0,
35
+ };
36
+ }
37
+ function dropPercent(config) {
38
+ const d = config.dropPercent;
39
+ return typeof d === "number" && Number.isFinite(d) && d > 0 && d < 100 ? d : DEFAULT_DROP_PERCENT;
40
+ }
41
+ function baselineRule(config) {
42
+ return config.baseline && BASELINE_RULES.includes(config.baseline)
43
+ ? config.baseline
44
+ : DEFAULT_BASELINE;
45
+ }
46
+ /** The price a drop is measured from under `rule`, or null before anything was seen. */
47
+ export function reference(state, rule) {
48
+ switch (rule) {
49
+ case "first":
50
+ return state.first;
51
+ case "last":
52
+ return state.last;
53
+ case "peak":
54
+ return state.peak;
55
+ }
56
+ }
57
+ function miss(ctx, state, reason) {
58
+ const misses = state.misses + 1;
59
+ const outcome = { state: { ...state, misses }, summary: `no price: ${reason}` };
60
+ if (misses === MISSES_BEFORE_TELLING) {
61
+ outcome.notify = {
62
+ text: `${ctx.task.title}: no price read from ${ctx.config.url} ${misses} polls in a row ` +
63
+ `(${reason}). Check the page or the pattern; reply done to stop tracking.`,
64
+ actions: ["done"],
65
+ };
66
+ }
67
+ return outcome;
68
+ }
69
+ export const price = defineTaskType({
70
+ id: "price",
71
+ lane: "notify",
72
+ capabilities: ["notify"],
73
+ schedule: ["poll"],
74
+ intake: {
75
+ options: [
76
+ { name: "url", description: "The product page", required: true, kind: "string" },
77
+ { name: "every", description: "Hours between checks", required: true, kind: "integer" },
78
+ {
79
+ name: "drop",
80
+ description: "Alert on a drop of at least this percent; default 10",
81
+ required: false,
82
+ kind: "number",
83
+ },
84
+ {
85
+ name: "baseline",
86
+ description: "Measure the drop from the first, last or peak price seen; default last",
87
+ required: false,
88
+ kind: "string",
89
+ },
90
+ {
91
+ name: "pattern",
92
+ description: "A regular expression whose first group is the price, if the page needs one",
93
+ required: false,
94
+ kind: "string",
95
+ },
96
+ ],
97
+ },
98
+ async run(ctx) {
99
+ const { fetch } = ctx.ports;
100
+ if (!fetch)
101
+ throw new Error("the price type needs the Fetch port; the host configured none");
102
+ const state = stateOf(ctx.state);
103
+ let body;
104
+ try {
105
+ const response = await fetch.get(ctx.config.url);
106
+ if (response.status !== 200)
107
+ return miss(ctx, state, `HTTP ${response.status}`);
108
+ body = response.body;
109
+ }
110
+ catch (err) {
111
+ return miss(ctx, state, `fetch failed: ${err instanceof Error ? err.message : String(err)}`);
112
+ }
113
+ const found = extractPrice(body, ctx.config.pattern);
114
+ if (!found)
115
+ return miss(ctx, state, "no price in the page");
116
+ const seen = found.value;
117
+ const currency = found.currency ?? ctx.config.currency ?? "";
118
+ const point = currency
119
+ ? { value: seen, unit: currency, note: "observed" }
120
+ : { value: seen, note: "observed" };
121
+ const rule = baselineRule(ctx.config);
122
+ const drop = dropPercent(ctx.config);
123
+ const ref = reference(state, rule);
124
+ if (ref === null) {
125
+ return {
126
+ state: { first: seen, last: seen, peak: seen, below: false, misses: 0, alerts: 0 },
127
+ series: [point],
128
+ notify: {
129
+ text: `Now tracking ${ctx.task.title} at ${money(seen, currency)}. I will say when it ` +
130
+ `drops ${drop}% or more from the ${rule} price seen. ${ctx.config.url}`,
131
+ actions: ["done"],
132
+ },
133
+ summary: `baseline ${money(seen, currency)}`,
134
+ };
135
+ }
136
+ const line = ref * (1 - drop / 100);
137
+ const below = seen <= line + 1e-9;
138
+ const crossing = below && !state.below;
139
+ const next = {
140
+ first: state.first ?? seen,
141
+ last: seen,
142
+ peak: Math.max(state.peak ?? seen, seen),
143
+ below,
144
+ misses: 0,
145
+ alerts: state.alerts + (crossing ? 1 : 0),
146
+ };
147
+ if (!crossing) {
148
+ return {
149
+ state: next,
150
+ series: [point],
151
+ summary: `${money(seen, currency)} (${rule} ${money(ref, currency)})`,
152
+ };
153
+ }
154
+ const pct = Math.round(((ref - seen) / ref) * 100);
155
+ return {
156
+ state: next,
157
+ series: [point],
158
+ notify: {
159
+ text: `${ctx.task.title}: ${money(seen, currency)}, down ${pct}% from ` +
160
+ `${money(ref, currency)} (${rule} seen). ${ctx.config.url}`,
161
+ actions: ["done"],
162
+ },
163
+ summary: `alert: ${money(seen, currency)}, down ${pct}% from ${money(ref, currency)}`,
164
+ };
165
+ },
166
+ async onReply(ctx) {
167
+ if (ctx.reply.kind === "done")
168
+ return { complete: true, summary: "stopped tracking" };
169
+ return {};
170
+ },
171
+ });
172
+ //# sourceMappingURL=price.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price.js","sourceRoot":"","sources":["../src/price.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Category 6 (plan section 1.2, city-hall#11): subscriptions, warranties, domains, memberships.
3
+ * A `period` schedule rolls from the renewal date, firing `leadDays` before it; the reminder
4
+ * asks keep / cancel / renewed; the decision and the amount are recorded per period -- the
5
+ * current row in the task's state, the history in replies and the series -- and a cancel ends
6
+ * the task. Notify lane, tier 0, no model.
7
+ */
8
+ export interface RenewalConfig {
9
+ /** What one period costs, as first entered. */
10
+ amount: number;
11
+ /** ISO 4217 code, e.g. `USD`. */
12
+ currency: string;
13
+ /** A line carried on every reminder: where to cancel, which account, ... */
14
+ note?: string;
15
+ }
16
+ export declare const RENEWAL_DECISIONS: readonly ["keep", "cancel", "renewed"];
17
+ export type RenewalDecision = (typeof RENEWAL_DECISIONS)[number];
18
+ export declare function isRenewalDecision(value: string): value is RenewalDecision;
19
+ /** The current row: the amount as last reported, and the last decision taken. */
20
+ export interface RenewalState {
21
+ amount: number;
22
+ decision: RenewalDecision | null;
23
+ decidedAt: string | null;
24
+ /** The period date the last decision was about. */
25
+ periodDate: string | null;
26
+ }
27
+ /** A decision reply's payload: `"keep"`, or `{ choice, amount? }` with a newly paid amount. */
28
+ export declare function decisionOf(payload: unknown): {
29
+ choice: RenewalDecision | null;
30
+ amount: number | null;
31
+ };
32
+ export declare const renewal: import("@rackbops/docket-core").TaskType<RenewalConfig>;
33
+ //# sourceMappingURL=renewal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renewal.d.ts","sourceRoot":"","sources":["../src/renewal.ts"],"names":[],"mappings":"AAaA;;;;;;GAMG;AAEH,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,iBAAiB,YAAI,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAA;AAEvE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,eAAe,CAEzE;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAA;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,mDAAmD;IACnD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAeD,+FAA+F;AAC/F,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG;IAC5C,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB,CAYA;AAuBD,eAAO,MAAM,OAAO,yDAyFlB,CAAA"}
@@ -0,0 +1,146 @@
1
+ import { daysUntil, defineTaskType, periodDate, wallClock, } from "@rackbops/docket-core";
2
+ import { money } from "./money.js";
3
+ import { snoozeUntil } from "./reminder.js";
4
+ export const RENEWAL_DECISIONS = ["keep", "cancel", "renewed"];
5
+ export function isRenewalDecision(value) {
6
+ return RENEWAL_DECISIONS.includes(value);
7
+ }
8
+ function stateOf(ctx) {
9
+ const s = ctx.state;
10
+ if (s && typeof s.amount === "number" && Number.isFinite(s.amount)) {
11
+ return {
12
+ amount: s.amount,
13
+ decision: s.decision ?? null,
14
+ decidedAt: s.decidedAt ?? null,
15
+ periodDate: s.periodDate ?? null,
16
+ };
17
+ }
18
+ return { amount: ctx.config.amount, decision: null, decidedAt: null, periodDate: null };
19
+ }
20
+ /** A decision reply's payload: `"keep"`, or `{ choice, amount? }` with a newly paid amount. */
21
+ export function decisionOf(payload) {
22
+ if (typeof payload === "string") {
23
+ return { choice: isRenewalDecision(payload) ? payload : null, amount: null };
24
+ }
25
+ if (typeof payload === "object" && payload !== null) {
26
+ const p = payload;
27
+ const choice = typeof p.choice === "string" && isRenewalDecision(p.choice) ? p.choice : null;
28
+ const amount = typeof p.amount === "number" && Number.isFinite(p.amount) && p.amount >= 0 ? p.amount : null;
29
+ return { choice, amount };
30
+ }
31
+ return { choice: null, amount: null };
32
+ }
33
+ /** The period date this occurrence is about: from the schedule, or the due day itself. */
34
+ function periodDateOf(ctx) {
35
+ const due = new Date(ctx.occurrence.dueAt);
36
+ const zone = ctx.owner.timeZone;
37
+ if (ctx.task.schedule?.kind === "period")
38
+ return periodDate(ctx.task.schedule, due, zone);
39
+ const w = wallClock(due, zone);
40
+ return `${w.year}-${String(w.month).padStart(2, "0")}-${String(w.day).padStart(2, "0")}`;
41
+ }
42
+ function plural(n, word) {
43
+ return `${n} ${word}${n === 1 ? "" : "s"}`;
44
+ }
45
+ function when(date, ctx) {
46
+ const days = daysUntil(date, ctx.now, ctx.owner.timeZone);
47
+ if (days === null || days > 0) {
48
+ return days === null ? `on ${date}` : `on ${date}, in ${plural(days, "day")}`;
49
+ }
50
+ return days === 0 ? `today, ${date}` : `on ${date}, ${plural(-days, "day")} ago`;
51
+ }
52
+ export const renewal = defineTaskType({
53
+ id: "renewal",
54
+ lane: "notify",
55
+ capabilities: ["notify"],
56
+ schedule: ["period", "once"],
57
+ intake: {
58
+ options: [
59
+ { name: "amount", description: "What one period costs", required: true, kind: "number" },
60
+ { name: "currency", description: "Currency code, e.g. USD", required: true, kind: "string" },
61
+ {
62
+ name: "renews",
63
+ description: "The next renewal or expiry date, YYYY-MM-DD",
64
+ required: true,
65
+ kind: "string",
66
+ },
67
+ {
68
+ name: "every",
69
+ description: "Periods between renewals (with unit); default 1",
70
+ required: false,
71
+ kind: "integer",
72
+ },
73
+ {
74
+ name: "unit",
75
+ description: "day, week, month or year; default year",
76
+ required: false,
77
+ kind: "string",
78
+ },
79
+ {
80
+ name: "lead",
81
+ description: "Days before the date to ask; default 7",
82
+ required: false,
83
+ kind: "integer",
84
+ },
85
+ { name: "note", description: "Carried on every reminder", required: false, kind: "string" },
86
+ ],
87
+ },
88
+ async run(ctx) {
89
+ const state = stateOf(ctx);
90
+ const date = periodDateOf(ctx);
91
+ const cost = money(state.amount, ctx.config.currency);
92
+ const lines = [
93
+ `${ctx.task.title} renews ${when(date, ctx)}: ${cost}. Keep it, cancel it, or mark it renewed.`,
94
+ ];
95
+ if (ctx.config.note)
96
+ lines.push(ctx.config.note);
97
+ const paid = ctx.history.series.filter((p) => p.note === "keep" || p.note === "renewed");
98
+ if (paid.length > 0) {
99
+ const total = paid.reduce((sum, p) => sum + p.value, 0);
100
+ lines.push(`So far: ${plural(paid.length, "period")}, ${money(total, ctx.config.currency)}.`);
101
+ }
102
+ if (state.decision && state.periodDate) {
103
+ lines.push(`Last time (${state.periodDate}): ${state.decision}.`);
104
+ }
105
+ return {
106
+ notify: {
107
+ text: lines.join("\n"),
108
+ actions: ["decision", "snooze"],
109
+ decisions: [...RENEWAL_DECISIONS],
110
+ },
111
+ summary: `asked: renews ${date} for ${cost}`,
112
+ };
113
+ },
114
+ async onReply(ctx) {
115
+ if (ctx.reply.kind === "snooze") {
116
+ const until = snoozeUntil(ctx.reply.payload, ctx.now);
117
+ return { snoozeUntil: until, summary: `snoozed until ${until.toISOString()}` };
118
+ }
119
+ if (ctx.reply.kind !== "decision")
120
+ return {};
121
+ const { choice, amount } = decisionOf(ctx.reply.payload);
122
+ if (!choice)
123
+ return { summary: `decision not understood; say ${RENEWAL_DECISIONS.join(", ")}` };
124
+ const previous = stateOf(ctx);
125
+ const date = periodDateOf(ctx);
126
+ const state = {
127
+ amount: amount ?? previous.amount,
128
+ decision: choice,
129
+ decidedAt: ctx.now.toISOString(),
130
+ periodDate: date,
131
+ };
132
+ if (choice === "cancel")
133
+ return { state, complete: true, summary: `cancelled before ${date}` };
134
+ const point = {
135
+ value: state.amount,
136
+ unit: ctx.config.currency,
137
+ note: choice,
138
+ };
139
+ return {
140
+ state,
141
+ series: [point],
142
+ summary: `${choice} for ${date} at ${money(state.amount, ctx.config.currency)}`,
143
+ };
144
+ },
145
+ });
146
+ //# sourceMappingURL=renewal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renewal.js","sourceRoot":"","sources":["../src/renewal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rackbops/docket-types",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "The tracker's task types: reminder, renewal, price, research, scout, wantlist. Built on @rackbops/docket-core.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  ],
30
30
  "sideEffects": false,
31
31
  "dependencies": {
32
- "@rackbops/docket-core": "^0.1.0"
32
+ "@rackbops/docket-core": "^0.2.0"
33
33
  },
34
34
  "scripts": {
35
35
  "build": "tsc",