@toa.io/extensions.chronicles 0.0.0-stage → 1.0.0-alpha.333

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 +22 -0
  2. package/components/chronicles.encounters/manifest.toa.yaml +108 -0
  3. package/components/chronicles.encounters/operations/anonymous.js +4 -0
  4. package/components/chronicles.encounters/operations/describe.js +4 -0
  5. package/components/chronicles.encounters/operations/identified.js +4 -0
  6. package/components/chronicles.encounters/types/index.d.ts +3 -0
  7. package/components/chronicles.encounters/types/toa.d.ts +36 -0
  8. package/components/chronicles.queries/manifest.toa.yaml +139 -0
  9. package/components/chronicles.queries/operations/define.js +3 -0
  10. package/components/chronicles.queries/operations/describe.js +3 -0
  11. package/components/chronicles.queries/operations/drop.js +3 -0
  12. package/components/chronicles.queries/operations/query.js +3 -0
  13. package/components/chronicles.queries/types/index.d.ts +3 -0
  14. package/components/chronicles.queries/types/toa.d.ts +49 -0
  15. package/components/chronicles.relay/manifest.toa.yaml +43 -0
  16. package/components/chronicles.relay/operations/encounter.js +4 -0
  17. package/components/chronicles.relay/operations/record.js +4 -0
  18. package/components/chronicles.relay/types/index.d.ts +3 -0
  19. package/components/chronicles.relay/types/toa.d.ts +24 -0
  20. package/guides/clickhouse.md +19 -0
  21. package/guides/log.md +52 -0
  22. package/package.json +39 -4
  23. package/readme.md +686 -0
  24. package/store/clickhouse.scope.sql +29 -0
  25. package/store/clickhouse.sql +99 -0
  26. package/transpiled/Client.d.ts +32 -0
  27. package/transpiled/Client.js +173 -0
  28. package/transpiled/Client.js.map +1 -0
  29. package/transpiled/Factory.d.ts +24 -0
  30. package/transpiled/Factory.js +62 -0
  31. package/transpiled/Factory.js.map +1 -0
  32. package/transpiled/Provider.d.ts +142 -0
  33. package/transpiled/Provider.js +4 -0
  34. package/transpiled/Provider.js.map +1 -0
  35. package/transpiled/Publisher.d.ts +50 -0
  36. package/transpiled/Publisher.js +169 -0
  37. package/transpiled/Publisher.js.map +1 -0
  38. package/transpiled/Reader.d.ts +28 -0
  39. package/transpiled/Reader.js +166 -0
  40. package/transpiled/Reader.js.map +1 -0
  41. package/transpiled/Recorder.d.ts +38 -0
  42. package/transpiled/Recorder.js +107 -0
  43. package/transpiled/Recorder.js.map +1 -0
  44. package/transpiled/Relay.d.ts +14 -0
  45. package/transpiled/Relay.js +27 -0
  46. package/transpiled/Relay.js.map +1 -0
  47. package/transpiled/Sender.d.ts +25 -0
  48. package/transpiled/Sender.js +86 -0
  49. package/transpiled/Sender.js.map +1 -0
  50. package/transpiled/Service.d.ts +26 -0
  51. package/transpiled/Service.js +70 -0
  52. package/transpiled/Service.js.map +1 -0
  53. package/transpiled/address.d.ts +7 -0
  54. package/transpiled/address.js +27 -0
  55. package/transpiled/address.js.map +1 -0
  56. package/transpiled/agent.d.ts +16 -0
  57. package/transpiled/agent.js +48 -0
  58. package/transpiled/agent.js.map +1 -0
  59. package/transpiled/clickhouse/Client.d.ts +35 -0
  60. package/transpiled/clickhouse/Client.js +71 -0
  61. package/transpiled/clickhouse/Client.js.map +1 -0
  62. package/transpiled/clickhouse/Provider.d.ts +35 -0
  63. package/transpiled/clickhouse/Provider.js +169 -0
  64. package/transpiled/clickhouse/Provider.js.map +1 -0
  65. package/transpiled/clickhouse/ddl.d.ts +9 -0
  66. package/transpiled/clickhouse/ddl.js +15 -0
  67. package/transpiled/clickhouse/ddl.js.map +1 -0
  68. package/transpiled/clickhouse/views.d.ts +11 -0
  69. package/transpiled/clickhouse/views.js +97 -0
  70. package/transpiled/clickhouse/views.js.map +1 -0
  71. package/transpiled/contract.d.ts +12 -0
  72. package/transpiled/contract.js +58 -0
  73. package/transpiled/contract.js.map +1 -0
  74. package/transpiled/index.d.ts +15 -0
  75. package/transpiled/index.js +2 -0
  76. package/transpiled/index.js.map +1 -0
  77. package/transpiled/settings.d.ts +13 -0
  78. package/transpiled/settings.js +48 -0
  79. package/transpiled/settings.js.map +1 -0
  80. package/transpiled/store.d.ts +5 -0
  81. package/transpiled/store.js +11 -0
  82. package/transpiled/store.js.map +1 -0
  83. package/README.md +0 -3
@@ -0,0 +1,169 @@
1
+ import { createConnection } from 'node:net';
2
+ import { connect } from 'comq';
3
+ import { console } from 'openspan';
4
+ import { Connector } from '@toa.io/core';
5
+ import { EXCHANGE } from '@toa.io/definitions/extensions.chronicles';
6
+ /**
7
+ * Rows to the broker of the log: each a JSON message to the exchange `chronicles` under the key of
8
+ * its table, with the scope of the context and when it expires. A publication resolves once the
9
+ * broker has confirmed it, which is what the caller acknowledges what it carried by.
10
+ *
11
+ * The connection is made by the first publication and not before, and nothing waits for it to
12
+ * start: while the broker is away, a publication waits for it, and what it carries waits, unacknowledged,
13
+ * where it came from.
14
+ */
15
+ export class Publisher extends Connector {
16
+ url;
17
+ scope;
18
+ retention;
19
+ io;
20
+ opened;
21
+ /** aborted as the publisher stops: what waits for the broker then gives up, to be delivered again */
22
+ closing = new AbortController();
23
+ stopped = false;
24
+ /** where it publishes, which a publisher of other settings does not */
25
+ address;
26
+ // eslint-disable-next-line max-params
27
+ constructor(address, user, scope, retention) {
28
+ super();
29
+ this.address = address;
30
+ const url = new URL(address);
31
+ url.username = encodeURIComponent(user.username);
32
+ url.password = encodeURIComponent(user.password);
33
+ this.url = url.href;
34
+ this.scope = scope;
35
+ this.retention = retention;
36
+ }
37
+ async publish(rows, states = [], encounters = []) {
38
+ if (this.stopped)
39
+ throw new Error('The chronicles are stopping');
40
+ const io = await this.connection();
41
+ await Promise.all([
42
+ ...rows.map(async (row) => await this.route(io, 'events', row, row.time)),
43
+ ...states.map(async (state) => await this.route(io, 'states', state)),
44
+ ...encounters.map(async (encounter) => await this.route(io, 'encounters', encounter, encounter.received))
45
+ ]);
46
+ }
47
+ /**
48
+ * Fails what waits for the broker, and what is published from now on, so the service publishing
49
+ * through it is not held open by what would wait for the broker past the service's own end: it
50
+ * is delivered again, to this process or another.
51
+ */
52
+ get done() {
53
+ return this.stopped;
54
+ }
55
+ stop() {
56
+ this.stopped = true;
57
+ this.closing.abort();
58
+ }
59
+ async open() {
60
+ this.stopped = false;
61
+ this.closing = new AbortController();
62
+ }
63
+ /**
64
+ * A connection still being made is not waited for — it may never be — but closed once it is,
65
+ * and what waits for it is failed, so it is delivered again, to this process or another.
66
+ */
67
+ async close() {
68
+ const { io, opened } = this;
69
+ this.io = undefined;
70
+ this.opened = undefined;
71
+ this.stop();
72
+ if (opened !== undefined)
73
+ await opened.close();
74
+ else
75
+ void io?.then(async (late) => await late.close(), () => undefined);
76
+ }
77
+ async connection() {
78
+ this.io ??= reachable(this.url, this.closing.signal)
79
+ .then(async () => await connect(this.url))
80
+ .then((io) => {
81
+ this.opened = io;
82
+ return io;
83
+ }, (error) => {
84
+ this.io = undefined;
85
+ console.warn('Chronicles failed to reach their broker', {
86
+ error: error instanceof Error ? error.message : String(error)
87
+ });
88
+ throw error;
89
+ });
90
+ const { signal } = this.closing;
91
+ // a publication waits for the broker a while, and then fails, so what it carries is
92
+ // delivered again rather than held: a process that stops is not kept by one that never ends
93
+ return await new Promise((resolve, reject) => {
94
+ function away() {
95
+ reject(new Error('The broker of the chronicles is away'));
96
+ }
97
+ function closing() {
98
+ reject(new Error('The chronicles are closing'));
99
+ }
100
+ const timer = setTimeout(away, WAIT);
101
+ signal.addEventListener('abort', closing, { once: true });
102
+ this.io.then(resolve, reject).finally(() => {
103
+ clearTimeout(timer);
104
+ signal.removeEventListener('abort', closing);
105
+ });
106
+ });
107
+ }
108
+ // eslint-disable-next-line max-params
109
+ async route(io, table, row, from) {
110
+ const body = message(row, this.scope, from === undefined ? null : expires(from, this.retention));
111
+ await io.route(EXCHANGE, table, body, 'application/json');
112
+ }
113
+ }
114
+ /**
115
+ * Resolves once the broker accepts a connection, trying again until it does or `signal` aborts.
116
+ * A connection is asked of comq only then: one it is asked for keeps trying for as long as it
117
+ * takes, and is no longer the publisher's to give up.
118
+ */
119
+ async function reachable(url, signal) {
120
+ const { protocol, hostname, port } = new URL(url);
121
+ const target = { host: hostname, port: Number(port === '' ? DEFAULT[protocol] : port) };
122
+ while (!signal.aborted) {
123
+ const reached = await new Promise((resolve) => {
124
+ const socket = createConnection(target);
125
+ function settle(ok) {
126
+ socket.destroy();
127
+ resolve(ok);
128
+ }
129
+ socket.setTimeout(PROBE);
130
+ socket.once('connect', () => settle(true));
131
+ socket.once('timeout', () => settle(false));
132
+ socket.once('error', () => settle(false));
133
+ });
134
+ if (reached)
135
+ return;
136
+ await new Promise((resolve) => setTimeout(resolve, PROBE).unref());
137
+ }
138
+ throw new Error('The chronicles are closing');
139
+ }
140
+ /**
141
+ * A row as the store reads it: of a scope, its times in the store's format, and when it expires,
142
+ * where it does.
143
+ */
144
+ export function message(row, scope, until) {
145
+ const body = { scope, ...row, time: timestamp(row.time) };
146
+ if ('received' in row)
147
+ body.received = timestamp(row.received);
148
+ if (until !== null)
149
+ body.expires = until;
150
+ return body;
151
+ }
152
+ /** When a row stops being kept: its time and the retention, or the end of the store's time. */
153
+ export function expires(from, retention) {
154
+ if (retention === undefined)
155
+ return FOREVER;
156
+ return timestamp(from + retention * DAY).slice(0, 19);
157
+ }
158
+ /** A `DateTime64(3)` as the store reads one without guessing: UTC, to the millisecond. */
159
+ export function timestamp(ms) {
160
+ return new Date(ms).toISOString().replace('T', ' ').replace('Z', '');
161
+ }
162
+ const DAY = 86_400_000;
163
+ /** milliseconds a publication waits for the broker */
164
+ const WAIT = 10_000;
165
+ /** milliseconds a try to reach the broker takes, and between two of them */
166
+ const PROBE = 1_000;
167
+ const DEFAULT = { 'amqp:': 5672, 'amqps:': 5671 };
168
+ const FOREVER = '2106-01-01 00:00:00';
169
+ //# sourceMappingURL=Publisher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Publisher.js","sourceRoot":"","sources":["../source/Publisher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { Connector } from '@toa.io/core';
2
+ import type { Provider } from './Provider.ts';
3
+ import type { extensions } from '@toa.io/core/types';
4
+ import type { Settings } from '@toa.io/definitions/extensions.chronicles';
5
+ /**
6
+ * What `chronicles.queries` reads the log through: queries in the provider's dialect, views made
7
+ * from a statement the provider let a query run, and what the log holds — how it is read, what the
8
+ * components declare, what the context documents of encounters, and what the store has written.
9
+ */
10
+ export declare class Reader extends Connector {
11
+ readonly name = "chronicles";
12
+ private readonly host;
13
+ private readonly provider;
14
+ private readonly settings;
15
+ private encounters?;
16
+ constructor(host: extensions.Host, provider: () => Provider, settings: () => Settings);
17
+ invoke(method: string, ...args: any[]): Promise<unknown>;
18
+ private describe;
19
+ private query;
20
+ private define;
21
+ private drop;
22
+ /**
23
+ * What the configuration of `chronicles.encounters` documents, asked of that component, which is
24
+ * in this service where the context serves encounters. Found on the first ask and not before: an
25
+ * aspect is made ahead of the components of its composition.
26
+ */
27
+ private documented;
28
+ }
@@ -0,0 +1,166 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { Connector, Locator, exceptions } from '@toa.io/core';
4
+ import { ENCOUNTERS, NAMESPACE } from '@toa.io/definitions/extensions.chronicles';
5
+ import { Refusal } from './Provider.js';
6
+ /**
7
+ * What `chronicles.queries` reads the log through: queries in the provider's dialect, views made
8
+ * from a statement the provider let a query run, and what the log holds — how it is read, what the
9
+ * components declare, what the context documents of encounters, and what the store has written.
10
+ */
11
+ export class Reader extends Connector {
12
+ name = 'chronicles';
13
+ host;
14
+ provider;
15
+ settings;
16
+ encounters;
17
+ constructor(host, provider, settings) {
18
+ super();
19
+ this.host = host;
20
+ this.provider = provider;
21
+ this.settings = settings;
22
+ }
23
+ async invoke(method, ...args) {
24
+ switch (method) {
25
+ case 'describe':
26
+ return await this.describe();
27
+ case 'query':
28
+ return await this.query(args[0]);
29
+ case 'define':
30
+ return await this.define(args[0]);
31
+ case 'drop':
32
+ return await this.drop(args[0]);
33
+ default:
34
+ throw new exceptions.MisuseException(`Chronicles have no '${method}'`);
35
+ }
36
+ }
37
+ async describe() {
38
+ const provider = this.provider();
39
+ const settings = this.settings();
40
+ const [written, kept, sent, views, documented] = await Promise.all([
41
+ provider.labels(),
42
+ provider.states(),
43
+ provider.encounters(),
44
+ provider.views(),
45
+ this.documented(settings)
46
+ ]);
47
+ const declared = { ...settings.events, ...settings.facts };
48
+ const stated = Object.fromEntries(Object.entries(settings.states).map(([label, contract]) => [
49
+ component(label),
50
+ contract
51
+ ]));
52
+ const labels = merge(written.map(({ label, ...stats }) => [label, stats]), Object.keys(declared)).map(([label, stats]) => ({
53
+ label,
54
+ ...about(declared[label], provider),
55
+ ...(stats ?? { rows: 0 })
56
+ }));
57
+ const states = merge(kept.map(({ component, ...stats }) => [component, stats]), Object.keys(stated)).map(([component, stats]) => ({
58
+ component,
59
+ ...about(stated[component], provider),
60
+ ...(stats ?? { entities: 0 })
61
+ }));
62
+ const encounters = merge(sent.map(({ name, ...stats }) => [name, stats]), Object.keys(documented)).map(([name, stats]) => {
63
+ const { description, fields: told = {} } = documented[name] ?? {};
64
+ const fields = (stats?.fields ?? []).map((field) => told[field.path] === undefined
65
+ ? field
66
+ : { ...field, description: told[field.path] });
67
+ return {
68
+ name,
69
+ ...(description === undefined ? {} : { description }),
70
+ ...(stats ?? { rows: 0 }),
71
+ fields
72
+ };
73
+ });
74
+ return { guide: `${GUIDE}\n${provider.guide}`, labels, states, encounters, views };
75
+ }
76
+ async query(sql) {
77
+ return await refused(async () => await this.provider().query(statement(sql)));
78
+ }
79
+ async define(definition) {
80
+ const provider = this.provider();
81
+ const { name } = definition;
82
+ if (provider.tables.includes(name))
83
+ return new Error('RESERVED', {
84
+ cause: { message: `'${name}' is a table of the log` }
85
+ });
86
+ const select = statement(definition.sql);
87
+ const invalid = await refused(async () => await provider.validate(select));
88
+ if (invalid instanceof Error)
89
+ return invalid;
90
+ const views = await provider.views();
91
+ if (!views.some((view) => view.name === name) && views.length >= MAX_VIEWS)
92
+ return new Error('EXHAUSTED', {
93
+ cause: { message: `A context has at most ${MAX_VIEWS} views` }
94
+ });
95
+ const defined = await refused(async () => await provider.define({ ...definition, sql: select }));
96
+ return defined instanceof Error ? defined : undefined;
97
+ }
98
+ async drop(name) {
99
+ const provider = this.provider();
100
+ const views = await provider.views();
101
+ if (!views.some((view) => view.name === name))
102
+ return new Error('NOT_FOUND');
103
+ await provider.drop(name);
104
+ return undefined;
105
+ }
106
+ /**
107
+ * What the configuration of `chronicles.encounters` documents, asked of that component, which is
108
+ * in this service where the context serves encounters. Found on the first ask and not before: an
109
+ * aspect is made ahead of the components of its composition.
110
+ */
111
+ async documented(settings) {
112
+ if (!settings.encounters)
113
+ return {};
114
+ this.encounters ??= (async () => {
115
+ const remote = await this.host.remote(LOCATOR, SOURCE);
116
+ this.depends(remote);
117
+ await remote.connect();
118
+ return remote;
119
+ })().catch((error) => {
120
+ this.encounters = undefined;
121
+ throw error;
122
+ });
123
+ const remote = await this.encounters;
124
+ return (await remote.invoke('describe', {}));
125
+ }
126
+ }
127
+ /**
128
+ * Of what was written and what is declared, by key, in order: a key declared and not yet written
129
+ * has no statistics, and one written and no longer declared has nothing else.
130
+ */
131
+ function merge(written, declared) {
132
+ const by = new Map(written);
133
+ const keys = [...new Set([...by.keys(), ...declared])].sort();
134
+ return keys.map((key) => [key, by.get(key)]);
135
+ }
136
+ /** What a declaration says of a label or a state: its meaning, and its fields. */
137
+ function about(contract, provider) {
138
+ if (contract === undefined)
139
+ return {};
140
+ return {
141
+ description: contract.description,
142
+ fields: provider.fields(contract.properties)
143
+ };
144
+ }
145
+ function component(label) {
146
+ return label.slice(0, label.lastIndexOf('.'));
147
+ }
148
+ /** One statement: a trailing semicolon is an agent's habit. */
149
+ function statement(sql) {
150
+ return sql.trim().replace(/;+$/, '').trim();
151
+ }
152
+ async function refused(fn) {
153
+ try {
154
+ return await fn();
155
+ }
156
+ catch (exception) {
157
+ if (exception instanceof Refusal)
158
+ return new Error('REFUSED', { cause: { message: exception.message } });
159
+ throw exception;
160
+ }
161
+ }
162
+ const LOCATOR = new Locator(ENCOUNTERS, NAMESPACE);
163
+ const SOURCE = { service: 'chronicles' };
164
+ const MAX_VIEWS = 50;
165
+ const GUIDE = readFileSync(resolve(import.meta.dirname, '../guides/log.md'), 'utf8');
166
+ //# sourceMappingURL=Reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Reader.js","sourceRoot":"","sources":["../source/Reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { Connector } from '@toa.io/core';
2
+ import type { Publisher } from './Publisher.ts';
3
+ import type { Row, State } from './Provider.ts';
4
+ import type { Contract } from '@toa.io/definitions/extensions.chronicles';
5
+ import type { Message } from '@toa.io/core/types';
6
+ /**
7
+ * The consumer of one event the log is kept of: a row of `events` for an event a component declares
8
+ * under `events`, and a state for the one it declares its `state`. A message carries what the event
9
+ * rendered, its id and when the change was made, so what is said of the entity — which, its
10
+ * version — is read from the payload, where the entity as it stands carries `id` and `VERSION`;
11
+ * when, from the property the component names, or the message; and what is written of the payload
12
+ * is what the contract declares.
13
+ */
14
+ export declare class Recorder extends Connector {
15
+ private readonly label;
16
+ private readonly component;
17
+ private readonly kind;
18
+ private readonly of;
19
+ private readonly publisher;
20
+ constructor(label: string, kind: Kind, of: Of, publisher: Publisher);
21
+ receive(message: Message): Promise<void>;
22
+ }
23
+ /** What a component declares of the payloads of a label. */
24
+ export interface Of {
25
+ contract: Contract;
26
+ /** the property that names whose a row is; `null` where none does */
27
+ identity: string | null;
28
+ /** the property that says when the change was made; `null` where none does */
29
+ timestamp: string | null;
30
+ }
31
+ export declare function row(label: string, component: string, message: Message, of: Of): Row;
32
+ /**
33
+ * The entity as its state event renders it. One that does not say which entity it is, and which
34
+ * version, cannot be kept, and no delivery of it will say otherwise: refused as a contract the
35
+ * component broke, so it is parked on its first delivery rather than retried.
36
+ */
37
+ export declare function state(label: string, component: string, message: Message, of: Of): State;
38
+ export type Kind = 'event' | 'state';
@@ -0,0 +1,107 @@
1
+ import { Connector, entities, exceptions } from '@toa.io/core';
2
+ import { Mismatch, project } from './contract.js';
3
+ /**
4
+ * The consumer of one event the log is kept of: a row of `events` for an event a component declares
5
+ * under `events`, and a state for the one it declares its `state`. A message carries what the event
6
+ * rendered, its id and when the change was made, so what is said of the entity — which, its
7
+ * version — is read from the payload, where the entity as it stands carries `id` and `VERSION`;
8
+ * when, from the property the component names, or the message; and what is written of the payload
9
+ * is what the contract declares.
10
+ */
11
+ export class Recorder extends Connector {
12
+ label;
13
+ component;
14
+ kind;
15
+ of;
16
+ publisher;
17
+ // eslint-disable-next-line max-params
18
+ constructor(label, kind, of, publisher) {
19
+ super();
20
+ this.label = label;
21
+ this.component = label.slice(0, label.lastIndexOf('.'));
22
+ this.kind = kind;
23
+ this.of = of;
24
+ this.publisher = publisher;
25
+ }
26
+ async receive(message) {
27
+ if (this.kind === 'state')
28
+ await this.publisher.publish([], [state(this.label, this.component, message, this.of)]);
29
+ else
30
+ await this.publisher.publish([row(this.label, this.component, message, this.of)]);
31
+ }
32
+ }
33
+ // eslint-disable-next-line max-params
34
+ export function row(label, component, message, of) {
35
+ const payload = object(message.payload);
36
+ const { id, VERSION } = payload;
37
+ return {
38
+ // a message from outside Toa carries no id, and is then the same message by what it says
39
+ id: message.id ?? entities.derive(label, JSON.stringify(payload)),
40
+ label,
41
+ component,
42
+ time: when(payload, message, of.timestamp),
43
+ entity: typeof id === 'string' ? id : null,
44
+ version: typeof VERSION === 'number' ? VERSION : null,
45
+ identity: whose(payload, of.identity),
46
+ trace: message.telemetry ?? null,
47
+ payload: declared(label, payload, of.contract)
48
+ };
49
+ }
50
+ /**
51
+ * The entity as its state event renders it. One that does not say which entity it is, and which
52
+ * version, cannot be kept, and no delivery of it will say otherwise: refused as a contract the
53
+ * component broke, so it is parked on its first delivery rather than retried.
54
+ */
55
+ // eslint-disable-next-line max-params
56
+ export function state(label, component, message, of) {
57
+ const payload = object(message.payload);
58
+ const { id, VERSION } = payload;
59
+ if (typeof id !== 'string' || typeof VERSION !== 'number')
60
+ throw new exceptions.EntityContractException(`The state '${label}' is an entity as it stands, and its payload carries no 'id' and 'VERSION'`);
61
+ return {
62
+ component,
63
+ entity: id,
64
+ version: VERSION,
65
+ identity: whose(payload, of.identity),
66
+ time: when(payload, message, of.timestamp),
67
+ payload: declared(label, payload, of.contract)
68
+ };
69
+ }
70
+ /**
71
+ * When the change was made: what the payload says in the property the component names, or else
72
+ * what the message says, which the process that made the change stamped as it made it. A message
73
+ * of a process from before messages said so says nothing, and is when it is taken.
74
+ */
75
+ function when(payload, message, property) {
76
+ const said = property === null ? undefined : payload[property];
77
+ if (typeof said === 'number')
78
+ return said;
79
+ return message.time ?? Date.now();
80
+ }
81
+ /**
82
+ * What the contract declares of a payload. One it does not admit is a contract the component broke,
83
+ * which no delivery will mend: refused, so it is parked on its first delivery.
84
+ */
85
+ function declared(label, payload, contract) {
86
+ try {
87
+ return project(payload, contract.properties);
88
+ }
89
+ catch (exception) {
90
+ if (exception instanceof Mismatch)
91
+ throw new exceptions.EntityContractException(`The payload of '${label}' is not what its chronicles declare: ${exception.message}`);
92
+ throw exception;
93
+ }
94
+ }
95
+ /** The identity a payload names in the property the component declares, where it is one. */
96
+ function whose(payload, property) {
97
+ if (property === null)
98
+ return null;
99
+ const value = payload[property];
100
+ return typeof value === 'string' ? value : null;
101
+ }
102
+ function object(payload) {
103
+ return typeof payload === 'object' && payload !== null && !Array.isArray(payload)
104
+ ? payload
105
+ : { value: payload };
106
+ }
107
+ //# sourceMappingURL=Recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Recorder.js","sourceRoot":"","sources":["../source/Recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Connector } from '@toa.io/core';
2
+ import type { Publisher } from './Publisher.ts';
3
+ /**
4
+ * What `chronicles.relay` publishes the tasks it is given through: a fact a component sent, and the
5
+ * encounters of a batch a client sent. A task is done once the broker has confirmed its rows. The
6
+ * publisher is the relay's own, closed with it: the component may be composed by another factory
7
+ * than the one of the service it runs in.
8
+ */
9
+ export declare class Relay extends Connector {
10
+ readonly name = "chronicles";
11
+ private readonly publisher;
12
+ constructor(publisher: Publisher);
13
+ invoke(method: string, ...args: any[]): Promise<void>;
14
+ }
@@ -0,0 +1,27 @@
1
+ import { Connector, exceptions } from '@toa.io/core';
2
+ /**
3
+ * What `chronicles.relay` publishes the tasks it is given through: a fact a component sent, and the
4
+ * encounters of a batch a client sent. A task is done once the broker has confirmed its rows. The
5
+ * publisher is the relay's own, closed with it: the component may be composed by another factory
6
+ * than the one of the service it runs in.
7
+ */
8
+ export class Relay extends Connector {
9
+ name = 'chronicles';
10
+ publisher;
11
+ constructor(publisher) {
12
+ super();
13
+ this.publisher = publisher;
14
+ this.depends(publisher);
15
+ }
16
+ async invoke(method, ...args) {
17
+ switch (method) {
18
+ case 'record':
19
+ return await this.publisher.publish([args[0]]);
20
+ case 'encounter':
21
+ return await this.publisher.publish([], [], args[0]);
22
+ default:
23
+ throw new exceptions.MisuseException(`Chronicles have no '${method}'`);
24
+ }
25
+ }
26
+ }
27
+ //# sourceMappingURL=Relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Relay.js","sourceRoot":"","sources":["../source/Relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIpD;;;;;GAKG;AACH,MAAM,OAAO,KAAM,SAAQ,SAAS;IAClB,IAAI,GAAG,YAAY,CAAA;IAElB,SAAS,CAAW;IAErC,YAAmB,SAAoB;QACrC,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACzB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,GAAG,IAAW;QAChD,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,QAAQ;gBACX,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAQ,CAAC,CAAC,CAAA;YACvD,KAAK,WAAW;gBACd,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAgB,CAAC,CAAA;YACrE;gBACE,MAAM,IAAI,UAAU,CAAC,eAAe,CAAC,uBAAuB,MAAM,GAAG,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,25 @@
1
+ import { Connector, Locator } from '@toa.io/core';
2
+ import type { Contract } from '@toa.io/definitions/extensions.chronicles';
3
+ import type { extensions } from '@toa.io/core/types';
4
+ /**
5
+ * `context.chronicles.send(name, payload, { identity })` — a fact that changes no state, written under
6
+ * `<component>.<name>`, of which what the component declares of it is sent. It is a task to the
7
+ * component that writes facts, so it resolves once the broker has it, and is kept until that
8
+ * component takes it.
9
+ */
10
+ export declare class Sender extends Connector {
11
+ readonly name = "chronicles";
12
+ private readonly host;
13
+ private readonly locator;
14
+ private readonly facts;
15
+ private remote?;
16
+ /** `facts` are the contracts of the facts the component declares, by name */
17
+ constructor(host: extensions.Host, locator: Locator, facts: Record<string, Contract>);
18
+ invoke(method: string, ...args: any[]): Promise<void>;
19
+ private send;
20
+ /**
21
+ * Resolved on the first fact and not before: an aspect is made ahead of the components of its
22
+ * composition, and one that could not be found is looked for again by the next fact.
23
+ */
24
+ private recorder;
25
+ }