@toa.io/extensions.chronicles 1.0.0-alpha.332 → 1.0.0-alpha.335
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/LICENSE +22 -0
- package/components/chronicles.encounters/manifest.toa.yaml +108 -0
- package/components/{chronicles.ingestion → chronicles.encounters}/operations/anonymous.js +1 -1
- package/components/chronicles.encounters/operations/describe.js +4 -0
- package/components/{chronicles.ingestion → chronicles.encounters}/operations/identified.js +1 -1
- package/components/{chronicles.ingestion → chronicles.encounters}/types/toa.d.ts +13 -0
- package/components/chronicles.queries/manifest.toa.yaml +39 -15
- package/components/chronicles.queries/operations/define.js +1 -1
- package/components/chronicles.queries/types/toa.d.ts +8 -3
- package/components/{chronicles.facts → chronicles.relay}/manifest.toa.yaml +14 -3
- package/components/chronicles.relay/operations/encounter.js +4 -0
- package/components/{chronicles.facts → chronicles.relay}/operations/record.js +1 -1
- package/components/{chronicles.facts → chronicles.relay}/types/toa.d.ts +5 -0
- package/guides/clickhouse.md +19 -0
- package/guides/log.md +52 -0
- package/package.json +8 -4
- package/readme.md +459 -142
- package/store/clickhouse.scope.sql +29 -0
- package/store/clickhouse.sql +99 -0
- package/transpiled/Client.d.ts +19 -6
- package/transpiled/Client.js +89 -37
- package/transpiled/Client.js.map +1 -1
- package/transpiled/Factory.d.ts +13 -5
- package/transpiled/Factory.js +32 -23
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Provider.d.ts +76 -25
- package/transpiled/Provider.js.map +1 -1
- package/transpiled/Publisher.d.ts +50 -0
- package/transpiled/Publisher.js +169 -0
- package/transpiled/Publisher.js.map +1 -0
- package/transpiled/Reader.d.ts +15 -3
- package/transpiled/Reader.js +98 -14
- package/transpiled/Reader.js.map +1 -1
- package/transpiled/Recorder.d.ts +21 -11
- package/transpiled/Recorder.js +49 -24
- package/transpiled/Recorder.js.map +1 -1
- package/transpiled/Relay.d.ts +14 -0
- package/transpiled/Relay.js +27 -0
- package/transpiled/Relay.js.map +1 -0
- package/transpiled/Sender.d.ts +8 -4
- package/transpiled/Sender.js +27 -10
- package/transpiled/Sender.js.map +1 -1
- package/transpiled/Service.d.ts +26 -0
- package/transpiled/Service.js +70 -0
- package/transpiled/Service.js.map +1 -0
- package/transpiled/agent.d.ts +16 -0
- package/transpiled/agent.js +48 -0
- package/transpiled/agent.js.map +1 -0
- package/transpiled/clickhouse/Client.d.ts +5 -8
- package/transpiled/clickhouse/Client.js +12 -14
- package/transpiled/clickhouse/Client.js.map +1 -1
- package/transpiled/clickhouse/Provider.d.ts +20 -18
- package/transpiled/clickhouse/Provider.js +97 -72
- package/transpiled/clickhouse/Provider.js.map +1 -1
- package/transpiled/clickhouse/ddl.d.ts +3 -20
- package/transpiled/clickhouse/ddl.js +6 -79
- package/transpiled/clickhouse/ddl.js.map +1 -1
- package/transpiled/clickhouse/views.d.ts +11 -0
- package/transpiled/clickhouse/views.js +97 -0
- package/transpiled/clickhouse/views.js.map +1 -0
- package/transpiled/contract.d.ts +12 -0
- package/transpiled/contract.js +58 -0
- package/transpiled/contract.js.map +1 -0
- package/transpiled/index.d.ts +9 -3
- package/transpiled/settings.d.ts +6 -4
- package/transpiled/settings.js +27 -14
- package/transpiled/settings.js.map +1 -1
- package/transpiled/store.d.ts +3 -3
- package/transpiled/store.js +3 -3
- package/transpiled/store.js.map +1 -1
- package/components/chronicles.ingestion/manifest.toa.yaml +0 -60
- package/transpiled/Facts.d.ts +0 -9
- package/transpiled/Facts.js +0 -16
- package/transpiled/Facts.js.map +0 -1
- package/transpiled/Scribe.d.ts +0 -25
- package/transpiled/Scribe.js +0 -61
- package/transpiled/Scribe.js.map +0 -1
- package/transpiled/Writer.d.ts +0 -14
- package/transpiled/Writer.js +0 -50
- package/transpiled/Writer.js.map +0 -1
- /package/components/{chronicles.facts → chronicles.encounters}/types/index.d.ts +0 -0
- /package/components/{chronicles.ingestion → chronicles.relay}/types/index.d.ts +0 -0
package/transpiled/Recorder.d.ts
CHANGED
|
@@ -1,28 +1,38 @@
|
|
|
1
1
|
import { Connector } from '@toa.io/core';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Publisher } from './Publisher.ts';
|
|
3
3
|
import type { Row, State } from './Provider.ts';
|
|
4
|
+
import type { Contract } from '@toa.io/definitions/extensions.chronicles';
|
|
4
5
|
import type { Message } from '@toa.io/core/types';
|
|
5
6
|
/**
|
|
6
|
-
* The consumer of one event the log is kept of: a row of `events` for an event a component
|
|
7
|
-
* under `events`, and a state for the one it
|
|
8
|
-
* rendered and
|
|
9
|
-
* the payload, where the entity as it stands carries `id
|
|
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.
|
|
10
13
|
*/
|
|
11
14
|
export declare class Recorder extends Connector {
|
|
12
15
|
private readonly label;
|
|
13
16
|
private readonly component;
|
|
14
17
|
private readonly kind;
|
|
15
|
-
private readonly
|
|
16
|
-
private readonly
|
|
17
|
-
|
|
18
|
-
constructor(label: string, kind: Kind, identity: string | null, writer: Writer);
|
|
18
|
+
private readonly of;
|
|
19
|
+
private readonly publisher;
|
|
20
|
+
constructor(label: string, kind: Kind, of: Of, publisher: Publisher);
|
|
19
21
|
receive(message: Message): Promise<void>;
|
|
20
22
|
}
|
|
21
|
-
|
|
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;
|
|
22
32
|
/**
|
|
23
33
|
* The entity as its state event renders it. One that does not say which entity it is, and which
|
|
24
34
|
* version, cannot be kept, and no delivery of it will say otherwise: refused as a contract the
|
|
25
35
|
* component broke, so it is parked on its first delivery rather than retried.
|
|
26
36
|
*/
|
|
27
|
-
export declare function state(label: string, component: string, message: Message,
|
|
37
|
+
export declare function state(label: string, component: string, message: Message, of: Of): State;
|
|
28
38
|
export type Kind = 'event' | 'state';
|
package/transpiled/Recorder.js
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
import { Connector, entities, exceptions } from '@toa.io/core';
|
|
2
|
+
import { Mismatch, project } from './contract.js';
|
|
2
3
|
/**
|
|
3
|
-
* The consumer of one event the log is kept of: a row of `events` for an event a component
|
|
4
|
-
* under `events`, and a state for the one it
|
|
5
|
-
* rendered and
|
|
6
|
-
* the payload, where the entity as it stands carries `id
|
|
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.
|
|
7
10
|
*/
|
|
8
11
|
export class Recorder extends Connector {
|
|
9
12
|
label;
|
|
10
13
|
component;
|
|
11
14
|
kind;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/** `identity` is the property of the payload that names whose a row is, where the component has one */
|
|
15
|
+
of;
|
|
16
|
+
publisher;
|
|
15
17
|
// eslint-disable-next-line max-params
|
|
16
|
-
constructor(label, kind,
|
|
18
|
+
constructor(label, kind, of, publisher) {
|
|
17
19
|
super();
|
|
18
20
|
this.label = label;
|
|
19
21
|
this.component = label.slice(0, label.lastIndexOf('.'));
|
|
20
22
|
this.kind = kind;
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
+
this.of = of;
|
|
24
|
+
this.publisher = publisher;
|
|
23
25
|
}
|
|
24
26
|
async receive(message) {
|
|
25
27
|
if (this.kind === 'state')
|
|
26
|
-
await this.
|
|
28
|
+
await this.publisher.publish([], [state(this.label, this.component, message, this.of)]);
|
|
27
29
|
else
|
|
28
|
-
await this.
|
|
30
|
+
await this.publisher.publish([row(this.label, this.component, message, this.of)]);
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
// eslint-disable-next-line max-params
|
|
32
|
-
export function row(label, component, message,
|
|
34
|
+
export function row(label, component, message, of) {
|
|
33
35
|
const payload = object(message.payload);
|
|
34
|
-
const { id, VERSION
|
|
36
|
+
const { id, VERSION } = payload;
|
|
35
37
|
return {
|
|
36
38
|
// a message from outside Toa carries no id, and is then the same message by what it says
|
|
37
39
|
id: message.id ?? entities.derive(label, JSON.stringify(payload)),
|
|
38
40
|
label,
|
|
39
41
|
component,
|
|
40
|
-
time:
|
|
42
|
+
time: when(payload, message, of.timestamp),
|
|
41
43
|
entity: typeof id === 'string' ? id : null,
|
|
42
44
|
version: typeof VERSION === 'number' ? VERSION : null,
|
|
43
|
-
identity: whose(payload, identity),
|
|
44
|
-
ip: null,
|
|
45
|
-
country: null,
|
|
45
|
+
identity: whose(payload, of.identity),
|
|
46
46
|
trace: message.telemetry ?? null,
|
|
47
|
-
payload
|
|
47
|
+
payload: declared(label, payload, of.contract)
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
@@ -53,20 +53,45 @@ export function row(label, component, message, identity = null) {
|
|
|
53
53
|
* component broke, so it is parked on its first delivery rather than retried.
|
|
54
54
|
*/
|
|
55
55
|
// eslint-disable-next-line max-params
|
|
56
|
-
export function state(label, component, message,
|
|
56
|
+
export function state(label, component, message, of) {
|
|
57
57
|
const payload = object(message.payload);
|
|
58
|
-
const { id, VERSION
|
|
58
|
+
const { id, VERSION } = payload;
|
|
59
59
|
if (typeof id !== 'string' || typeof VERSION !== 'number')
|
|
60
60
|
throw new exceptions.EntityContractException(`The state '${label}' is an entity as it stands, and its payload carries no 'id' and 'VERSION'`);
|
|
61
61
|
return {
|
|
62
62
|
component,
|
|
63
63
|
entity: id,
|
|
64
64
|
version: VERSION,
|
|
65
|
-
identity: whose(payload, identity),
|
|
66
|
-
time:
|
|
67
|
-
payload
|
|
65
|
+
identity: whose(payload, of.identity),
|
|
66
|
+
time: when(payload, message, of.timestamp),
|
|
67
|
+
payload: declared(label, payload, of.contract)
|
|
68
68
|
};
|
|
69
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
|
+
}
|
|
70
95
|
/** The identity a payload names in the property the component declares, where it is one. */
|
|
71
96
|
function whose(payload, property) {
|
|
72
97
|
if (property === null)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Recorder.js","sourceRoot":"","sources":["../source/Recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;
|
|
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"}
|
package/transpiled/Sender.d.ts
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { Connector, Locator } from '@toa.io/core';
|
|
2
|
+
import type { Contract } from '@toa.io/definitions/extensions.chronicles';
|
|
2
3
|
import type { extensions } from '@toa.io/core/types';
|
|
3
4
|
/**
|
|
4
5
|
* `context.chronicles.send(name, payload, { identity })` — a fact that changes no state, written under
|
|
5
|
-
* `<component>.<name
|
|
6
|
-
* broker has it, and is kept until that
|
|
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.
|
|
7
9
|
*/
|
|
8
10
|
export declare class Sender extends Connector {
|
|
9
11
|
readonly name = "chronicles";
|
|
10
12
|
private readonly host;
|
|
11
13
|
private readonly locator;
|
|
14
|
+
private readonly facts;
|
|
12
15
|
private remote?;
|
|
13
|
-
|
|
16
|
+
/** `facts` are the contracts of the facts the component declares, by name */
|
|
17
|
+
constructor(host: extensions.Host, locator: Locator, facts: Record<string, Contract>);
|
|
14
18
|
invoke(method: string, ...args: any[]): Promise<void>;
|
|
15
19
|
private send;
|
|
16
20
|
/**
|
|
17
21
|
* Resolved on the first fact and not before: an aspect is made ahead of the components of its
|
|
18
22
|
* composition, and one that could not be found is looked for again by the next fact.
|
|
19
23
|
*/
|
|
20
|
-
private
|
|
24
|
+
private recorder;
|
|
21
25
|
}
|
package/transpiled/Sender.js
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { current, encode } from 'openspan';
|
|
2
2
|
import { Connector, Locator, entities, exceptions } from '@toa.io/core';
|
|
3
|
-
import {
|
|
3
|
+
import { NAMESPACE, RELAY } from '@toa.io/definitions/extensions.chronicles';
|
|
4
|
+
import { Mismatch, project } from './contract.js';
|
|
4
5
|
/**
|
|
5
6
|
* `context.chronicles.send(name, payload, { identity })` — a fact that changes no state, written under
|
|
6
|
-
* `<component>.<name
|
|
7
|
-
* broker has it, and is kept until that
|
|
7
|
+
* `<component>.<name>`, of which what the component declares of it is sent. It is a task to the
|
|
8
|
+
* component that writes facts, so it resolves once the broker has it, and is kept until that
|
|
9
|
+
* component takes it.
|
|
8
10
|
*/
|
|
9
11
|
export class Sender extends Connector {
|
|
10
12
|
name = 'chronicles';
|
|
11
13
|
host;
|
|
12
14
|
locator;
|
|
15
|
+
facts;
|
|
13
16
|
remote;
|
|
14
|
-
|
|
17
|
+
/** `facts` are the contracts of the facts the component declares, by name */
|
|
18
|
+
constructor(host, locator, facts) {
|
|
15
19
|
super();
|
|
16
20
|
this.host = host;
|
|
17
21
|
this.locator = locator;
|
|
22
|
+
this.facts = facts;
|
|
18
23
|
}
|
|
19
24
|
// named rather than forwarded, so that the connector's lifecycle is not reachable from an
|
|
20
25
|
// algorithm
|
|
@@ -29,6 +34,9 @@ export class Sender extends Connector {
|
|
|
29
34
|
if (payload !== undefined &&
|
|
30
35
|
(typeof payload !== 'object' || payload === null || Array.isArray(payload)))
|
|
31
36
|
throw new exceptions.MisuseException('The payload of a chronicle is an object');
|
|
37
|
+
const contract = this.facts[name];
|
|
38
|
+
if (contract === undefined)
|
|
39
|
+
throw new exceptions.MisuseException(`'${this.locator.id}' declares no fact '${name}'`);
|
|
32
40
|
const context = current();
|
|
33
41
|
const row = {
|
|
34
42
|
id: entities.newid(),
|
|
@@ -38,19 +46,17 @@ export class Sender extends Connector {
|
|
|
38
46
|
entity: null,
|
|
39
47
|
version: null,
|
|
40
48
|
identity: typeof options?.identity === 'string' ? options.identity : null,
|
|
41
|
-
ip: null,
|
|
42
|
-
country: null,
|
|
43
49
|
trace: context === undefined ? null : encode(context),
|
|
44
|
-
payload: payload ?? {}
|
|
50
|
+
payload: declared(name, payload ?? {}, contract)
|
|
45
51
|
};
|
|
46
|
-
const remote = await this.
|
|
52
|
+
const remote = await this.recorder();
|
|
47
53
|
await remote.invoke('record', { input: row, task: true });
|
|
48
54
|
}
|
|
49
55
|
/**
|
|
50
56
|
* Resolved on the first fact and not before: an aspect is made ahead of the components of its
|
|
51
57
|
* composition, and one that could not be found is looked for again by the next fact.
|
|
52
58
|
*/
|
|
53
|
-
async
|
|
59
|
+
async recorder() {
|
|
54
60
|
this.remote ??= (async () => {
|
|
55
61
|
const remote = await this.host.remote(LOCATOR, SOURCE);
|
|
56
62
|
this.depends(remote);
|
|
@@ -63,7 +69,18 @@ export class Sender extends Connector {
|
|
|
63
69
|
return await this.remote;
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
|
-
|
|
72
|
+
/** What the contract declares of the payload of a fact; one it does not admit is not sent. */
|
|
73
|
+
function declared(name, payload, contract) {
|
|
74
|
+
try {
|
|
75
|
+
return project(payload, contract.properties);
|
|
76
|
+
}
|
|
77
|
+
catch (exception) {
|
|
78
|
+
if (exception instanceof Mismatch)
|
|
79
|
+
throw new exceptions.MisuseException(`The fact '${name}' is not what its chronicles declare: ${exception.message}`);
|
|
80
|
+
throw exception;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const LOCATOR = new Locator(RELAY, NAMESPACE);
|
|
67
84
|
const SOURCE = { service: 'chronicles' };
|
|
68
85
|
const NAME = /^[a-zA-Z][a-zA-Z0-9]*(\.[a-zA-Z][a-zA-Z0-9]*)*$/;
|
|
69
86
|
//# sourceMappingURL=Sender.js.map
|
package/transpiled/Sender.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sender.js","sourceRoot":"","sources":["../source/Sender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACvE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"Sender.js","sourceRoot":"","sources":["../source/Sender.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Connector } from '@toa.io/core';
|
|
2
|
+
import type { Publisher } from './Publisher.ts';
|
|
3
|
+
import type { Settings } from '@toa.io/definitions/extensions.chronicles';
|
|
4
|
+
import type { extensions } from '@toa.io/core/types';
|
|
5
|
+
/**
|
|
6
|
+
* The extension's own service: a consumer of every event the log is kept of — one group for every
|
|
7
|
+
* replica, so each event is published once — and the components it ships: the queries, the relay,
|
|
8
|
+
* and the encounters where the annotation asks for them. Nothing of it waits for the broker the
|
|
9
|
+
* rows are published to.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Service extends Connector {
|
|
12
|
+
private readonly host;
|
|
13
|
+
private readonly settings;
|
|
14
|
+
private readonly publisher;
|
|
15
|
+
constructor(host: extensions.Host, settings: Settings, publisher: Publisher);
|
|
16
|
+
/** first of all, so what waits for the broker of the log holds up none of what closes after */
|
|
17
|
+
protected close(): Promise<void>;
|
|
18
|
+
protected open(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/** The components this extension ships, which run inside its own service. */
|
|
21
|
+
export declare function components(): Components;
|
|
22
|
+
interface Components {
|
|
23
|
+
labels: string[];
|
|
24
|
+
paths: string[];
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { Connector } from '@toa.io/core';
|
|
4
|
+
import { ENCOUNTERS, NAMESPACE, SERVICE } from '@toa.io/definitions/extensions.chronicles';
|
|
5
|
+
import { Recorder } from './Recorder.js';
|
|
6
|
+
/**
|
|
7
|
+
* The extension's own service: a consumer of every event the log is kept of — one group for every
|
|
8
|
+
* replica, so each event is published once — and the components it ships: the queries, the relay,
|
|
9
|
+
* and the encounters where the annotation asks for them. Nothing of it waits for the broker the
|
|
10
|
+
* rows are published to.
|
|
11
|
+
*/
|
|
12
|
+
export class Service extends Connector {
|
|
13
|
+
host;
|
|
14
|
+
settings;
|
|
15
|
+
publisher;
|
|
16
|
+
// eslint-disable-next-line max-params
|
|
17
|
+
constructor(host, settings, publisher) {
|
|
18
|
+
super();
|
|
19
|
+
this.host = host;
|
|
20
|
+
this.settings = settings;
|
|
21
|
+
this.publisher = publisher;
|
|
22
|
+
this.depends(publisher);
|
|
23
|
+
}
|
|
24
|
+
/** first of all, so what waits for the broker of the log holds up none of what closes after */
|
|
25
|
+
async close() {
|
|
26
|
+
this.publisher.stop();
|
|
27
|
+
}
|
|
28
|
+
async open() {
|
|
29
|
+
const consumed = [
|
|
30
|
+
...Object.entries(this.settings.events).map(([label, contract]) => [label, 'event', contract]),
|
|
31
|
+
...Object.entries(this.settings.states).map(([label, contract]) => [label, 'state', contract])
|
|
32
|
+
];
|
|
33
|
+
for (const [label, kind, contract] of consumed) {
|
|
34
|
+
// an event that is both a row and a state is two consumers, each of a group of its own
|
|
35
|
+
const group = kind === 'state' ? `${GROUP}.states` : GROUP;
|
|
36
|
+
const of = {
|
|
37
|
+
contract,
|
|
38
|
+
identity: this.settings.identities[component(label)] ?? null,
|
|
39
|
+
timestamp: this.settings.timestamps[component(label)] ?? null
|
|
40
|
+
};
|
|
41
|
+
const recorder = new Recorder(label, kind, of, this.publisher);
|
|
42
|
+
const consumer = await this.host.receive(label, group, recorder);
|
|
43
|
+
this.depends(consumer);
|
|
44
|
+
await consumer.connect();
|
|
45
|
+
}
|
|
46
|
+
const composition = await this.host.composition(paths(this.settings));
|
|
47
|
+
this.depends(composition);
|
|
48
|
+
await composition.connect();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** The components this extension ships, which run inside its own service. */
|
|
52
|
+
export function components() {
|
|
53
|
+
const labels = [];
|
|
54
|
+
const paths = [];
|
|
55
|
+
for (const entry of readdirSync(ROOT, { withFileTypes: true }))
|
|
56
|
+
if (entry.isDirectory()) {
|
|
57
|
+
labels.push(entry.name.replace('.', '-'));
|
|
58
|
+
paths.push(resolve(ROOT, entry.name));
|
|
59
|
+
}
|
|
60
|
+
return { labels, paths };
|
|
61
|
+
}
|
|
62
|
+
function component(label) {
|
|
63
|
+
return label.slice(0, label.lastIndexOf('.'));
|
|
64
|
+
}
|
|
65
|
+
function paths(settings) {
|
|
66
|
+
return components().paths.filter((path) => settings.encounters || !path.endsWith(`${NAMESPACE}.${ENCOUNTERS}`));
|
|
67
|
+
}
|
|
68
|
+
const ROOT = resolve(import.meta.dirname, '../components/');
|
|
69
|
+
const GROUP = `${NAMESPACE}.${SERVICE}`;
|
|
70
|
+
//# sourceMappingURL=Service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Service.js","sourceRoot":"","sources":["../source/Service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a client is, as its `user-agent` says, in the few words a question of who visits asks for:
|
|
3
|
+
* the browser, the operating system and the kind of device, by name and without versions — which
|
|
4
|
+
* browsers freeze or leave out, and which make an agent more of a fingerprint. Each is the first of
|
|
5
|
+
* its list that matches, so a name another agent copies comes after the one that copies it: Edge
|
|
6
|
+
* says Chrome and Safari, Chrome says Safari. What matches nothing is `null`.
|
|
7
|
+
*
|
|
8
|
+
* Safari on an iPad says it is a Mac, as it has since iPadOS 13, and nothing the request carries
|
|
9
|
+
* says otherwise: it is a desktop running macOS here.
|
|
10
|
+
*/
|
|
11
|
+
export declare function classify(agent: unknown): Classified;
|
|
12
|
+
export interface Classified {
|
|
13
|
+
browser: string | null;
|
|
14
|
+
os: string | null;
|
|
15
|
+
device: string | null;
|
|
16
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a client is, as its `user-agent` says, in the few words a question of who visits asks for:
|
|
3
|
+
* the browser, the operating system and the kind of device, by name and without versions — which
|
|
4
|
+
* browsers freeze or leave out, and which make an agent more of a fingerprint. Each is the first of
|
|
5
|
+
* its list that matches, so a name another agent copies comes after the one that copies it: Edge
|
|
6
|
+
* says Chrome and Safari, Chrome says Safari. What matches nothing is `null`.
|
|
7
|
+
*
|
|
8
|
+
* Safari on an iPad says it is a Mac, as it has since iPadOS 13, and nothing the request carries
|
|
9
|
+
* says otherwise: it is a desktop running macOS here.
|
|
10
|
+
*/
|
|
11
|
+
export function classify(agent) {
|
|
12
|
+
if (typeof agent !== 'string' || agent === '')
|
|
13
|
+
return { browser: null, os: null, device: null };
|
|
14
|
+
return {
|
|
15
|
+
browser: first(BROWSERS, agent),
|
|
16
|
+
os: first(SYSTEMS, agent),
|
|
17
|
+
device: first(DEVICES, agent) ?? 'desktop'
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function first(list, agent) {
|
|
21
|
+
return list.find(([, pattern]) => pattern.test(agent))?.[0] ?? null;
|
|
22
|
+
}
|
|
23
|
+
const BROWSERS = [
|
|
24
|
+
['Edge', /\bEdg(e|A|iOS)?\//],
|
|
25
|
+
['Opera', /\bOPR\/|\bOPT\/|\bOpera\b/],
|
|
26
|
+
['Samsung Internet', /\bSamsungBrowser\//],
|
|
27
|
+
['Firefox', /\bFirefox\/|\bFxiOS\//],
|
|
28
|
+
['Chrome', /(\b|Headless)Chrome\/|\bCriOS\//],
|
|
29
|
+
['Safari', /\bVersion\/[\d.]+.*\bSafari\//]
|
|
30
|
+
];
|
|
31
|
+
const SYSTEMS = [
|
|
32
|
+
['Windows', /\bWindows\b/],
|
|
33
|
+
['iOS', /\b(iPhone|iPad|iPod)\b/],
|
|
34
|
+
['macOS', /\bMac OS X\b|\bMacintosh\b/],
|
|
35
|
+
['Android', /\bAndroid\b/],
|
|
36
|
+
['ChromeOS', /\bCrOS\b/],
|
|
37
|
+
['Linux', /\bLinux\b/]
|
|
38
|
+
];
|
|
39
|
+
// a desktop is what is none of these, so it is not listed
|
|
40
|
+
const DEVICES = [
|
|
41
|
+
[
|
|
42
|
+
'bot',
|
|
43
|
+
/bot\b|crawl|spider|slurp|preview|headless|^curl\/|^wget\/|^python-|^go-http|^okhttp/i
|
|
44
|
+
],
|
|
45
|
+
['tablet', /\biPad\b|\bTablet\b|\bAndroid\b(?!.*\bMobile\b)/],
|
|
46
|
+
['mobile', /\bMobile\b|\biPhone\b|\biPod\b/]
|
|
47
|
+
];
|
|
48
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../source/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE;QAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IAElD,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC/B,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC;QACzB,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,SAAS;KAC3C,CAAA;AACH,CAAC;AAQD,SAAS,KAAK,CAAC,IAA6B,EAAE,KAAa;IACzD,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AACrE,CAAC;AAED,MAAM,QAAQ,GAA4B;IACxC,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAC7B,CAAC,OAAO,EAAE,2BAA2B,CAAC;IACtC,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;IAC1C,CAAC,SAAS,EAAE,uBAAuB,CAAC;IACpC,CAAC,QAAQ,EAAE,iCAAiC,CAAC;IAC7C,CAAC,QAAQ,EAAE,+BAA+B,CAAC;CAC5C,CAAA;AAED,MAAM,OAAO,GAA4B;IACvC,CAAC,SAAS,EAAE,aAAa,CAAC;IAC1B,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACjC,CAAC,OAAO,EAAE,4BAA4B,CAAC;IACvC,CAAC,SAAS,EAAE,aAAa,CAAC;IAC1B,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,OAAO,EAAE,WAAW,CAAC;CACvB,CAAA;AAED,0DAA0D;AAC1D,MAAM,OAAO,GAA4B;IACvC;QACE,KAAK;QACL,sFAAsF;KACvF;IACD,CAAC,QAAQ,EAAE,iDAAiD,CAAC;IAC7D,CAAC,QAAQ,EAAE,gCAAgC,CAAC;CAC7C,CAAA"}
|
|
@@ -3,22 +3,19 @@
|
|
|
3
3
|
* needs of it is a statement and its answer.
|
|
4
4
|
*/
|
|
5
5
|
export declare class ClickHouse {
|
|
6
|
-
readonly database: string;
|
|
7
6
|
private readonly endpoint;
|
|
8
7
|
private readonly headers;
|
|
8
|
+
/** The statements run in the user's default database, which is what the store gives it to read. */
|
|
9
9
|
constructor(address: string, user: string, key: string);
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
* is not this client's: `''` for a statement that must run before the database exists.
|
|
13
|
-
*/
|
|
14
|
-
run(sql: string, settings?: Record<string, string>, database?: string): Promise<void>;
|
|
10
|
+
/** A statement that answers nothing worth reading. */
|
|
11
|
+
run(sql: string, settings?: Record<string, string>): Promise<void>;
|
|
15
12
|
/**
|
|
16
13
|
* A query, answered as `JSONCompact`: the columns, and the rows as arrays. The format is the
|
|
17
14
|
* default rather than a clause, which a statement like `EXPLAIN` would take as its own.
|
|
18
15
|
*/
|
|
19
16
|
select(sql: string, settings?: Record<string, string>): Promise<Answer>;
|
|
20
|
-
/** Each row as an object, for what this extension reads of the
|
|
21
|
-
rows<T = Record<string, unknown>>(sql: string): Promise<T[]>;
|
|
17
|
+
/** Each row as an object, for what this extension reads of the store itself. */
|
|
18
|
+
rows<T = Record<string, unknown>>(sql: string, settings?: Record<string, string>): Promise<T[]>;
|
|
22
19
|
private request;
|
|
23
20
|
}
|
|
24
21
|
export interface Answer {
|
|
@@ -3,21 +3,17 @@
|
|
|
3
3
|
* needs of it is a statement and its answer.
|
|
4
4
|
*/
|
|
5
5
|
export class ClickHouse {
|
|
6
|
-
database;
|
|
7
6
|
endpoint;
|
|
8
7
|
headers;
|
|
8
|
+
/** The statements run in the user's default database, which is what the store gives it to read. */
|
|
9
9
|
constructor(address, user, key) {
|
|
10
10
|
const url = new URL(address);
|
|
11
|
-
this.database = decodeURIComponent(url.pathname.replace(/^\/+|\/+$/g, ''));
|
|
12
11
|
this.endpoint = `${url.protocol}//${url.host}/`;
|
|
13
12
|
this.headers = { 'x-clickhouse-user': user, 'x-clickhouse-key': key };
|
|
14
13
|
}
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
async run(sql, settings = {}, database = this.database) {
|
|
20
|
-
await this.request(sql, settings, database);
|
|
14
|
+
/** A statement that answers nothing worth reading. */
|
|
15
|
+
async run(sql, settings = {}) {
|
|
16
|
+
await this.request(sql, settings);
|
|
21
17
|
}
|
|
22
18
|
/**
|
|
23
19
|
* A query, answered as `JSONCompact`: the columns, and the rows as arrays. The format is the
|
|
@@ -27,15 +23,17 @@ export class ClickHouse {
|
|
|
27
23
|
const text = await this.request(sql, { ...settings, default_format: 'JSONCompact' });
|
|
28
24
|
return parse(text);
|
|
29
25
|
}
|
|
30
|
-
/** Each row as an object, for what this extension reads of the
|
|
31
|
-
async rows(sql) {
|
|
32
|
-
const text = await this.request(sql, {
|
|
26
|
+
/** Each row as an object, for what this extension reads of the store itself. */
|
|
27
|
+
async rows(sql, settings = {}) {
|
|
28
|
+
const text = await this.request(sql, {
|
|
29
|
+
...settings,
|
|
30
|
+
...NUMBERS,
|
|
31
|
+
default_format: 'JSON'
|
|
32
|
+
});
|
|
33
33
|
return parse(text).data;
|
|
34
34
|
}
|
|
35
|
-
async request(sql, settings
|
|
35
|
+
async request(sql, settings) {
|
|
36
36
|
const search = new URLSearchParams(settings);
|
|
37
|
-
if (database !== '')
|
|
38
|
-
search.set('database', database);
|
|
39
37
|
const response = await fetch(`${this.endpoint}?${search.toString()}`, {
|
|
40
38
|
method: 'POST',
|
|
41
39
|
headers: this.headers,
|