@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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- A context's user of the store: it reads the rows of its scope and nothing else, and keeps its
|
|
2
|
+
-- views in a database of its own. {scope} is the context's scope, {password} the `store_password`
|
|
3
|
+
-- of its `chronicles` secret.
|
|
4
|
+
|
|
5
|
+
CREATE DATABASE IF NOT EXISTS `{scope}`;
|
|
6
|
+
|
|
7
|
+
-- limits a query cannot lift, because they are its profile's, not settings a request passes
|
|
8
|
+
CREATE SETTINGS PROFILE IF NOT EXISTS `{scope}` SETTINGS
|
|
9
|
+
max_execution_time = 30 CONST,
|
|
10
|
+
max_result_rows = 10001 CONST,
|
|
11
|
+
max_result_bytes = 67108864 CONST,
|
|
12
|
+
max_memory_usage = 4000000000 CONST,
|
|
13
|
+
max_concurrent_queries_for_user = 4 CONST;
|
|
14
|
+
|
|
15
|
+
CREATE USER IF NOT EXISTS `{scope}` IDENTIFIED WITH sha256_password BY '{password}'
|
|
16
|
+
DEFAULT DATABASE chronicles SETTINGS PROFILE '{scope}';
|
|
17
|
+
|
|
18
|
+
GRANT SELECT ON chronicles.events TO `{scope}`;
|
|
19
|
+
GRANT SELECT ON chronicles.states TO `{scope}`;
|
|
20
|
+
GRANT SELECT ON chronicles.encounters TO `{scope}`;
|
|
21
|
+
GRANT SELECT, CREATE VIEW, CREATE TABLE, INSERT, DROP VIEW, DROP TABLE ON `{scope}`.* TO `{scope}`;
|
|
22
|
+
|
|
23
|
+
-- when its materialized views were last refreshed
|
|
24
|
+
GRANT SELECT ON system.view_refreshes TO `{scope}`;
|
|
25
|
+
|
|
26
|
+
-- replaced rather than kept: a policy outlives its user, and one left to a user made again would
|
|
27
|
+
-- apply to no one, which reads as no restriction at all
|
|
28
|
+
CREATE ROW POLICY OR REPLACE `{scope}` ON chronicles.events, chronicles.states, chronicles.encounters
|
|
29
|
+
USING scope = '{scope}' TO `{scope}`;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
-- The store of every context that publishes to one analytics broker: what Toa publishes, and what
|
|
2
|
+
-- it reads. Toa runs none of this; whoever sets the store up does, once, and again where a release
|
|
3
|
+
-- of Toa says so. {broker}, {broker_username} and {broker_password} are the broker ClickHouse
|
|
4
|
+
-- consumes, as ClickHouse reaches it.
|
|
5
|
+
|
|
6
|
+
CREATE DATABASE IF NOT EXISTS chronicles;
|
|
7
|
+
|
|
8
|
+
-- a row for everything a component did, by `id`, kept until `expires`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS chronicles.events (
|
|
10
|
+
scope LowCardinality(String),
|
|
11
|
+
id String,
|
|
12
|
+
label LowCardinality(String),
|
|
13
|
+
component LowCardinality(String),
|
|
14
|
+
time DateTime64(3, 'UTC'),
|
|
15
|
+
entity Nullable(String),
|
|
16
|
+
version Nullable(UInt64),
|
|
17
|
+
identity Nullable(String),
|
|
18
|
+
trace Nullable(String),
|
|
19
|
+
payload JSON,
|
|
20
|
+
expires DateTime DEFAULT '2106-01-01 00:00:00'
|
|
21
|
+
) ENGINE = ReplacingMergeTree
|
|
22
|
+
PARTITION BY toYYYYMM(time)
|
|
23
|
+
ORDER BY (scope, label, toDate(time), id)
|
|
24
|
+
TTL expires;
|
|
25
|
+
|
|
26
|
+
-- each entity as it stands, by the latest `version`
|
|
27
|
+
CREATE TABLE IF NOT EXISTS chronicles.states (
|
|
28
|
+
scope LowCardinality(String),
|
|
29
|
+
component LowCardinality(String),
|
|
30
|
+
entity String,
|
|
31
|
+
version UInt64,
|
|
32
|
+
identity Nullable(String),
|
|
33
|
+
time DateTime64(3, 'UTC'),
|
|
34
|
+
payload JSON
|
|
35
|
+
) ENGINE = ReplacingMergeTree(version)
|
|
36
|
+
ORDER BY (scope, component, entity);
|
|
37
|
+
|
|
38
|
+
-- what clients sent, by `id`, kept and expired by when the gateway received it
|
|
39
|
+
CREATE TABLE IF NOT EXISTS chronicles.encounters (
|
|
40
|
+
scope LowCardinality(String),
|
|
41
|
+
id String,
|
|
42
|
+
name LowCardinality(String),
|
|
43
|
+
time DateTime64(3, 'UTC'),
|
|
44
|
+
received DateTime64(3, 'UTC'),
|
|
45
|
+
visitor Nullable(String),
|
|
46
|
+
session Nullable(String),
|
|
47
|
+
identity Nullable(String),
|
|
48
|
+
ip Nullable(String),
|
|
49
|
+
country LowCardinality(Nullable(String)),
|
|
50
|
+
origin LowCardinality(Nullable(String)),
|
|
51
|
+
agent Nullable(String),
|
|
52
|
+
browser LowCardinality(Nullable(String)),
|
|
53
|
+
os LowCardinality(Nullable(String)),
|
|
54
|
+
device LowCardinality(Nullable(String)),
|
|
55
|
+
payload JSON,
|
|
56
|
+
expires DateTime DEFAULT '2106-01-01 00:00:00'
|
|
57
|
+
) ENGINE = ReplacingMergeTree
|
|
58
|
+
PARTITION BY toYYYYMM(received)
|
|
59
|
+
ORDER BY (scope, name, toDate(time), id)
|
|
60
|
+
TTL expires;
|
|
61
|
+
|
|
62
|
+
-- what the broker delivers: one JSON row a message, from the exchange `chronicles` under the key
|
|
63
|
+
-- of its table, moved into the table by the view beside it
|
|
64
|
+
CREATE TABLE IF NOT EXISTS chronicles.events_queue AS chronicles.events
|
|
65
|
+
ENGINE = RabbitMQ SETTINGS
|
|
66
|
+
rabbitmq_host_port = '{broker}', rabbitmq_username = '{broker_username}',
|
|
67
|
+
rabbitmq_password = '{broker_password}', rabbitmq_exchange_name = 'chronicles',
|
|
68
|
+
rabbitmq_exchange_type = 'direct', rabbitmq_routing_key_list = 'events',
|
|
69
|
+
rabbitmq_queue_base = 'chronicles.events', rabbitmq_format = 'JSONEachRow',
|
|
70
|
+
rabbitmq_flush_interval_ms = 1000;
|
|
71
|
+
|
|
72
|
+
-- a message without `expires` reads it as zero rather than as the default, which would expire it
|
|
73
|
+
CREATE MATERIALIZED VIEW IF NOT EXISTS chronicles.events_consumer TO chronicles.events
|
|
74
|
+
AS SELECT * REPLACE (if(toUnixTimestamp(expires) = 0, toDateTime('2106-01-01 00:00:00'), expires) AS expires)
|
|
75
|
+
FROM chronicles.events_queue;
|
|
76
|
+
|
|
77
|
+
CREATE TABLE IF NOT EXISTS chronicles.states_queue AS chronicles.states
|
|
78
|
+
ENGINE = RabbitMQ SETTINGS
|
|
79
|
+
rabbitmq_host_port = '{broker}', rabbitmq_username = '{broker_username}',
|
|
80
|
+
rabbitmq_password = '{broker_password}', rabbitmq_exchange_name = 'chronicles',
|
|
81
|
+
rabbitmq_exchange_type = 'direct', rabbitmq_routing_key_list = 'states',
|
|
82
|
+
rabbitmq_queue_base = 'chronicles.states', rabbitmq_format = 'JSONEachRow',
|
|
83
|
+
rabbitmq_flush_interval_ms = 1000;
|
|
84
|
+
|
|
85
|
+
CREATE MATERIALIZED VIEW IF NOT EXISTS chronicles.states_consumer TO chronicles.states
|
|
86
|
+
AS SELECT * FROM chronicles.states_queue;
|
|
87
|
+
|
|
88
|
+
CREATE TABLE IF NOT EXISTS chronicles.encounters_queue AS chronicles.encounters
|
|
89
|
+
ENGINE = RabbitMQ SETTINGS
|
|
90
|
+
rabbitmq_host_port = '{broker}', rabbitmq_username = '{broker_username}',
|
|
91
|
+
rabbitmq_password = '{broker_password}', rabbitmq_exchange_name = 'chronicles',
|
|
92
|
+
rabbitmq_exchange_type = 'direct', rabbitmq_routing_key_list = 'encounters',
|
|
93
|
+
rabbitmq_queue_base = 'chronicles.encounters', rabbitmq_format = 'JSONEachRow',
|
|
94
|
+
rabbitmq_flush_interval_ms = 1000;
|
|
95
|
+
|
|
96
|
+
-- a message without `expires` reads it as zero rather than as the default, which would expire it
|
|
97
|
+
CREATE MATERIALIZED VIEW IF NOT EXISTS chronicles.encounters_consumer TO chronicles.encounters
|
|
98
|
+
AS SELECT * REPLACE (if(toUnixTimestamp(expires) = 0, toDateTime('2106-01-01 00:00:00'), expires) AS expires)
|
|
99
|
+
FROM chronicles.encounters_queue;
|
package/transpiled/Client.d.ts
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { Connector } from '@toa.io/core';
|
|
2
|
+
import type { extensions } from '@toa.io/core/types';
|
|
2
3
|
import type { Address } from '@toa.io/definitions/extensions.chronicles';
|
|
3
|
-
import type { Writer } from './Writer.ts';
|
|
4
4
|
/**
|
|
5
|
-
* What `chronicles.
|
|
6
|
-
* with the client's own id and
|
|
5
|
+
* What `chronicles.encounters` takes what clients send through: each event a row of `encounters`,
|
|
6
|
+
* with the client's own id and the visitor and the session the batch names, all of a batch one
|
|
7
|
+
* task to the relay, and answered once the context's broker has it — the relay publishes it, then
|
|
8
|
+
* or once the broker of the log is back.
|
|
9
|
+
*
|
|
10
|
+
* A client's clock is its own, and may be off by anything; what it measures between two of its
|
|
11
|
+
* moments is not. So an event is when the gateway received the batch, less how long before
|
|
12
|
+
* sending it the client says it happened: the time of the gateway, at the distance the client
|
|
13
|
+
* measured.
|
|
7
14
|
*/
|
|
8
15
|
export declare class Client extends Connector {
|
|
9
16
|
readonly name = "chronicles";
|
|
10
|
-
private readonly
|
|
17
|
+
private readonly host;
|
|
11
18
|
private readonly address;
|
|
12
|
-
|
|
19
|
+
private relay?;
|
|
20
|
+
constructor(host: extensions.Host, address: () => Address);
|
|
13
21
|
invoke(method: string, ...args: any[]): Promise<unknown>;
|
|
14
22
|
/**
|
|
15
23
|
* `identity` is the one the gateway verified the caller to be, from the path; `null` for the
|
|
16
24
|
* route anyone may call, whose body is then refused where it says whose it is.
|
|
17
25
|
*/
|
|
18
|
-
private
|
|
26
|
+
private write;
|
|
27
|
+
/**
|
|
28
|
+
* Found on the first batch and not before: an aspect is made ahead of the components of its
|
|
29
|
+
* composition, and one that could not be found is looked for again by the next batch.
|
|
30
|
+
*/
|
|
31
|
+
private relayed;
|
|
19
32
|
}
|
package/transpiled/Client.js
CHANGED
|
@@ -1,73 +1,118 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { AGENT, INGESTION, NAMESPACE } from '@toa.io/definitions/extensions.chronicles';
|
|
1
|
+
import { Connector, Locator, exceptions } from '@toa.io/core';
|
|
2
|
+
import { NAMESPACE, RELAY } from '@toa.io/definitions/extensions.chronicles';
|
|
4
3
|
import { keep } from './address.js';
|
|
4
|
+
import { classify } from './agent.js';
|
|
5
5
|
/**
|
|
6
|
-
* What `chronicles.
|
|
7
|
-
* with the client's own id and
|
|
6
|
+
* What `chronicles.encounters` takes what clients send through: each event a row of `encounters`,
|
|
7
|
+
* with the client's own id and the visitor and the session the batch names, all of a batch one
|
|
8
|
+
* task to the relay, and answered once the context's broker has it — the relay publishes it, then
|
|
9
|
+
* or once the broker of the log is back.
|
|
10
|
+
*
|
|
11
|
+
* A client's clock is its own, and may be off by anything; what it measures between two of its
|
|
12
|
+
* moments is not. So an event is when the gateway received the batch, less how long before
|
|
13
|
+
* sending it the client says it happened: the time of the gateway, at the distance the client
|
|
14
|
+
* measured.
|
|
8
15
|
*/
|
|
9
16
|
export class Client extends Connector {
|
|
10
17
|
name = 'chronicles';
|
|
11
|
-
|
|
18
|
+
host;
|
|
12
19
|
address;
|
|
13
|
-
|
|
20
|
+
relay;
|
|
21
|
+
constructor(host, address) {
|
|
14
22
|
super();
|
|
15
|
-
this.
|
|
23
|
+
this.host = host;
|
|
16
24
|
this.address = address;
|
|
17
25
|
}
|
|
18
26
|
async invoke(method, ...args) {
|
|
19
|
-
if (method !== '
|
|
27
|
+
if (method !== 'write')
|
|
20
28
|
throw new exceptions.MisuseException(`Chronicles have no '${method}'`);
|
|
21
|
-
return await this.
|
|
29
|
+
return await this.write(args[0], args[1] ?? null);
|
|
22
30
|
}
|
|
23
31
|
/**
|
|
24
32
|
* `identity` is the one the gateway verified the caller to be, from the path; `null` for the
|
|
25
33
|
* route anyone may call, whose body is then refused where it says whose it is.
|
|
26
34
|
*/
|
|
27
|
-
async
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const trace = context === undefined ? null : encode(context);
|
|
35
|
+
async write(input, identity) {
|
|
36
|
+
const received = Date.now();
|
|
37
|
+
const batch = parse(input, identity !== null);
|
|
38
|
+
if (batch instanceof Error)
|
|
39
|
+
return batch;
|
|
33
40
|
// put in by the gateway, which overwrites whatever the body says of them
|
|
34
|
-
const { ip, country } = input;
|
|
41
|
+
const { ip, country, origin, agent } = input;
|
|
35
42
|
const address = keep(ip, this.address());
|
|
36
43
|
const from = typeof country === 'string' ? country : null;
|
|
37
|
-
const
|
|
44
|
+
const page = typeof origin === 'string' ? origin : null;
|
|
45
|
+
const client = typeof agent === 'string' ? agent : null;
|
|
46
|
+
const { browser, os, device } = classify(client);
|
|
47
|
+
const encounters = batch.events.map((event) => ({
|
|
38
48
|
id: event.id,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
name: event.name,
|
|
50
|
+
time: received - (batch.sent - event.time),
|
|
51
|
+
received,
|
|
52
|
+
visitor: batch.visitor,
|
|
53
|
+
session: batch.session,
|
|
44
54
|
identity,
|
|
45
55
|
ip: address,
|
|
46
56
|
country: from,
|
|
47
|
-
|
|
57
|
+
origin: page,
|
|
58
|
+
agent: client,
|
|
59
|
+
browser,
|
|
60
|
+
os,
|
|
61
|
+
device,
|
|
48
62
|
payload: event.payload ?? {}
|
|
49
63
|
}));
|
|
50
|
-
await this.
|
|
51
|
-
|
|
64
|
+
const relay = await this.relayed();
|
|
65
|
+
await relay.invoke('encounter', { input: { encounters }, task: true });
|
|
66
|
+
return { status: 202, accepted: encounters.length };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Found on the first batch and not before: an aspect is made ahead of the components of its
|
|
70
|
+
* composition, and one that could not be found is looked for again by the next batch.
|
|
71
|
+
*/
|
|
72
|
+
async relayed() {
|
|
73
|
+
this.relay ??= (async () => {
|
|
74
|
+
const remote = await this.host.remote(LOCATOR, SOURCE);
|
|
75
|
+
this.depends(remote);
|
|
76
|
+
await remote.connect();
|
|
77
|
+
return remote;
|
|
78
|
+
})().catch((error) => {
|
|
79
|
+
this.relay = undefined;
|
|
80
|
+
throw error;
|
|
81
|
+
});
|
|
82
|
+
return await this.relay;
|
|
52
83
|
}
|
|
53
84
|
}
|
|
54
85
|
/**
|
|
55
|
-
* A batch, `{ events }`: as JSON, or as the form `navigator.sendBeacon`
|
|
56
|
-
* the JSON of them. Checked here rather than by the operation's schema, which
|
|
57
|
-
* form's. `identified` is whether the gateway put the identity in, which is then a
|
|
58
|
-
* may have beside `events`.
|
|
86
|
+
* A batch, `{ sent, visitor?, session?, events }`: as JSON, or as the form `navigator.sendBeacon`
|
|
87
|
+
* sends, whose `events` is the JSON of them, and `sent` the digits of a number. Checked here rather than by the operation's schema, which
|
|
88
|
+
* cannot read the form's. `identified` is whether the gateway put the identity in, which is then a
|
|
89
|
+
* key the body may have beside `events`.
|
|
59
90
|
*/
|
|
60
91
|
function parse(input, identified) {
|
|
61
92
|
if (typeof input !== 'object' || input === null || Array.isArray(input))
|
|
62
|
-
return invalid('The body is `{ events }`');
|
|
63
|
-
// `ip` and `
|
|
64
|
-
|
|
93
|
+
return invalid('The body is `{ sent, visitor?, session?, events }`');
|
|
94
|
+
// `ip`, `country`, `origin` and `agent` are the gateway's, which sets them on every request it
|
|
95
|
+
// makes
|
|
96
|
+
const { events: given, sent: stated, visitor, session, identity, ip: _ip, country: _country, origin: _origin, agent: _agent, ...rest } = input;
|
|
65
97
|
const extra = Object.keys(rest);
|
|
66
98
|
if (extra.length > 0)
|
|
67
99
|
return invalid(`The body has no '${extra[0]}'`);
|
|
68
100
|
// the identity is the path's, never the body's
|
|
69
101
|
if (!identified && identity !== undefined)
|
|
70
102
|
return invalid('The body names no identity: a client that has one sends it in the path');
|
|
103
|
+
// `null` is a client that has none, as absent is
|
|
104
|
+
for (const [key, value] of [
|
|
105
|
+
['visitor', visitor],
|
|
106
|
+
['session', session]
|
|
107
|
+
])
|
|
108
|
+
if (value !== undefined &&
|
|
109
|
+
value !== null &&
|
|
110
|
+
(typeof value !== 'string' || value === '' || value.length > 64))
|
|
111
|
+
return invalid(`\`${key}\` is a string of 1 to 64 characters`);
|
|
112
|
+
// a form's every value is a string
|
|
113
|
+
const sent = typeof stated === 'string' && /^\d{1,15}$/.test(stated) ? Number(stated) : stated;
|
|
114
|
+
if (!Number.isInteger(sent) || sent < 0)
|
|
115
|
+
return invalid('`sent` is when the client sent the batch, in milliseconds by its clock');
|
|
71
116
|
let events = given;
|
|
72
117
|
if (typeof events === 'string')
|
|
73
118
|
try {
|
|
@@ -84,12 +129,19 @@ function parse(input, identified) {
|
|
|
84
129
|
const problem = check(event);
|
|
85
130
|
if (problem !== null)
|
|
86
131
|
return invalid(problem, index);
|
|
132
|
+
if (event.time > sent)
|
|
133
|
+
return invalid('An event happens before the batch that carries it is sent', index);
|
|
87
134
|
if (JSON.stringify(event.payload ?? {}).length > MAX_PAYLOAD)
|
|
88
135
|
return new Error('OVERSIZED', {
|
|
89
136
|
cause: { index, message: `A payload is at most ${MAX_PAYLOAD} bytes of JSON` }
|
|
90
137
|
});
|
|
91
138
|
}
|
|
92
|
-
return
|
|
139
|
+
return {
|
|
140
|
+
sent: sent,
|
|
141
|
+
visitor: visitor ?? null,
|
|
142
|
+
session: session ?? null,
|
|
143
|
+
events: events
|
|
144
|
+
};
|
|
93
145
|
}
|
|
94
146
|
function check(event) {
|
|
95
147
|
if (typeof event !== 'object' || event === null || Array.isArray(event))
|
|
@@ -102,7 +154,7 @@ function check(event) {
|
|
|
102
154
|
if (typeof name !== 'string' || name.length > 64 || !NAME.test(name))
|
|
103
155
|
return '`name` is lowercase letters, digits and underscores from a letter, at most 64 characters';
|
|
104
156
|
if (!Number.isInteger(time) || time < 0)
|
|
105
|
-
return
|
|
157
|
+
return "`time` is when it happened, in milliseconds by the client's clock";
|
|
106
158
|
if (payload !== undefined &&
|
|
107
159
|
(typeof payload !== 'object' || payload === null || Array.isArray(payload)))
|
|
108
160
|
return '`payload` is an object';
|
|
@@ -113,9 +165,9 @@ function invalid(message, index) {
|
|
|
113
165
|
cause: index === undefined ? { message } : { index, message }
|
|
114
166
|
});
|
|
115
167
|
}
|
|
116
|
-
const COMPONENT = `${NAMESPACE}.${INGESTION}`;
|
|
117
168
|
const MAX_EVENTS = 100;
|
|
118
169
|
const MAX_PAYLOAD = 16 * 1024;
|
|
119
|
-
// no dot: a label is `agent.<name>`, and what follows the prefix is one name
|
|
120
170
|
const NAME = /^[a-z][a-z0-9_]*$/;
|
|
171
|
+
const LOCATOR = new Locator(RELAY, NAMESPACE);
|
|
172
|
+
const SOURCE = { service: 'chronicles' };
|
|
121
173
|
//# sourceMappingURL=Client.js.map
|
package/transpiled/Client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Client.js","sourceRoot":"","sources":["../source/Client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"Client.js","sourceRoot":"","sources":["../source/Client.ts"],"names":[],"mappings":"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"}
|
package/transpiled/Factory.d.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import type { Connector, Locator } from '@toa.io/core';
|
|
2
2
|
import type { extensions } from '@toa.io/core/types';
|
|
3
|
+
import type { Declaration } from '@toa.io/definitions/extensions.chronicles';
|
|
3
4
|
/**
|
|
4
|
-
* A component sends facts through its context; the events it
|
|
5
|
+
* A component sends facts through its context; the events it declares reach the log as events of
|
|
5
6
|
* the context, which the service consumes. The components this extension ships are given what
|
|
6
|
-
* they serve instead: the queries, the
|
|
7
|
+
* they serve instead: the queries, the store; the relay, the publisher; the encounters, the relay.
|
|
7
8
|
*/
|
|
8
9
|
export declare class Factory implements extensions.Factory {
|
|
9
10
|
private readonly host;
|
|
10
|
-
/**
|
|
11
|
+
/** the publisher of the service running, which the relay it runs publishes through */
|
|
12
|
+
private publisher;
|
|
13
|
+
/** one store for the process, made where the process is one that reads it */
|
|
11
14
|
private store;
|
|
12
15
|
constructor(host: extensions.Host);
|
|
13
|
-
aspect(locator: Locator): extensions.Aspect;
|
|
16
|
+
aspect(locator: Locator, declaration?: Declaration): extensions.Aspect;
|
|
14
17
|
service(): Connector;
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The publisher of the settings there are, made anew where they have changed or the one there was
|
|
20
|
+
* has stopped with its service.
|
|
21
|
+
*/
|
|
22
|
+
private publish;
|
|
23
|
+
private read;
|
|
16
24
|
}
|
package/transpiled/Factory.js
CHANGED
|
@@ -1,48 +1,57 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { environment } from '@toa.io/generic';
|
|
2
|
+
import { ENCOUNTERS, NAMESPACE, QUERIES, RELAY } from '@toa.io/definitions/extensions.chronicles';
|
|
2
3
|
import { Client } from './Client.js';
|
|
3
|
-
import {
|
|
4
|
+
import { Publisher } from './Publisher.js';
|
|
4
5
|
import { Reader } from './Reader.js';
|
|
5
|
-
import {
|
|
6
|
+
import { Relay } from './Relay.js';
|
|
6
7
|
import { Sender } from './Sender.js';
|
|
7
|
-
import {
|
|
8
|
+
import { Service } from './Service.js';
|
|
8
9
|
import { credentials, settings } from './settings.js';
|
|
9
10
|
import { provider } from './store.js';
|
|
10
11
|
/**
|
|
11
|
-
* A component sends facts through its context; the events it
|
|
12
|
+
* A component sends facts through its context; the events it declares reach the log as events of
|
|
12
13
|
* the context, which the service consumes. The components this extension ships are given what
|
|
13
|
-
* they serve instead: the queries, the
|
|
14
|
+
* they serve instead: the queries, the store; the relay, the publisher; the encounters, the relay.
|
|
14
15
|
*/
|
|
15
16
|
export class Factory {
|
|
16
17
|
host;
|
|
17
|
-
/**
|
|
18
|
+
/** the publisher of the service running, which the relay it runs publishes through */
|
|
19
|
+
publisher;
|
|
20
|
+
/** one store for the process, made where the process is one that reads it */
|
|
18
21
|
store;
|
|
19
22
|
constructor(host) {
|
|
20
23
|
this.host = host;
|
|
21
24
|
}
|
|
22
|
-
aspect(locator) {
|
|
25
|
+
aspect(locator, declaration) {
|
|
23
26
|
if (mine(locator))
|
|
24
27
|
switch (locator.name) {
|
|
25
28
|
case QUERIES:
|
|
26
|
-
return new Reader(() => this.
|
|
27
|
-
case
|
|
28
|
-
return new
|
|
29
|
-
case
|
|
30
|
-
return new Client(
|
|
29
|
+
return new Reader(this.host, () => this.read(), settings);
|
|
30
|
+
case RELAY:
|
|
31
|
+
return new Relay(this.publish());
|
|
32
|
+
case ENCOUNTERS:
|
|
33
|
+
return new Client(this.host, () => settings().ip);
|
|
31
34
|
}
|
|
32
|
-
return new Sender(this.host, locator);
|
|
35
|
+
return new Sender(this.host, locator, declaration?.facts ?? {});
|
|
33
36
|
}
|
|
34
37
|
// nothing connects to this, so a halt takes the whole of it
|
|
35
38
|
service() {
|
|
36
|
-
return this.host.gate(async () =>
|
|
37
|
-
const { provider, writer } = this.keep();
|
|
38
|
-
return new Scribe(this.host, settings(), provider, writer);
|
|
39
|
-
});
|
|
39
|
+
return this.host.gate(async () => new Service(this.host, settings(), this.publish()));
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The publisher of the settings there are, made anew where they have changed or the one there was
|
|
43
|
+
* has stopped with its service.
|
|
44
|
+
*/
|
|
45
|
+
publish() {
|
|
46
|
+
const { broker, retention } = settings();
|
|
47
|
+
if (this.publisher === undefined ||
|
|
48
|
+
this.publisher.done ||
|
|
49
|
+
this.publisher.address !== broker)
|
|
50
|
+
this.publisher = new Publisher(broker, credentials().broker, environment.scope(), retention);
|
|
51
|
+
return this.publisher;
|
|
52
|
+
}
|
|
53
|
+
read() {
|
|
54
|
+
this.store ??= provider(settings(), credentials().store, environment.scope());
|
|
46
55
|
return this.store;
|
|
47
56
|
}
|
|
48
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EACL,UAAU,EACV,SAAS,EACT,OAAO,EACP,KAAK,EACN,MAAM,2CAA2C,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAMrC;;;;GAIG;AACH,MAAM,OAAO,OAAO;IACD,IAAI,CAAiB;IAEtC,sFAAsF;IAC9E,SAAS,CAAuB;IAExC,6EAA6E;IACrE,KAAK,CAAsB;IAEnC,YAAmB,IAAqB;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAEM,MAAM,CAAC,OAAgB,EAAE,WAAyB;QACvD,IAAI,IAAI,CAAC,OAAO,CAAC;YACf,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,KAAK,OAAO;oBACV,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAA;gBAC3D,KAAK,KAAK;oBACR,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;gBAClC,KAAK,UAAU;oBACb,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;YACrD,CAAC;QAEH,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAA;IACjE,CAAC;IAED,4DAA4D;IACrD,OAAO;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IACvF,CAAC;IAED;;;OAGG;IACK,OAAO;QACb,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAAA;QAExC,IACE,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,IAAI,CAAC,SAAS,CAAC,IAAI;YACnB,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,MAAM;YAEjC,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAC5B,MAAM,EACN,WAAW,EAAE,CAAC,MAAM,EACpB,WAAW,CAAC,KAAK,EAAE,EACnB,SAAS,CACV,CAAA;QAEH,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,CAAA;QAE7E,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;CACF;AAED,4FAA4F;AAC5F,SAAS,IAAI,CAAC,OAAgB;IAC5B,OAAO,OAAO,CAAC,SAAS,KAAK,SAAS,CAAA;AACxC,CAAC"}
|