h1v3 0.7.1 → 0.7.2
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/dist/browser/client/context.js +12 -13
- package/dist/browser/client/event-store.js +10 -3
- package/dist/browser/client/logger.js +6 -9
- package/package.json +1 -1
- package/src/client/context.js +9 -7
- package/src/client/node.js +24 -7
- package/src/commands/generate-rules.js +17 -5
- package/src/configuration.js +71 -14
- package/src/event-store/configuration.js +2 -2
- package/src/exec-eventstore.js +1 -33
- package/src/index.js +3 -1
- package/src/load-configuration.js +6 -2
- package/src/observability/index.js +2 -4
|
@@ -20,21 +20,20 @@ function defaultHandleError(err) {
|
|
|
20
20
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export async function inject({ database,
|
|
23
|
+
export async function inject({ database, authentication, metaURL, handleError = defaultHandleError }) {
|
|
24
24
|
|
|
25
25
|
if (!database) throw new Error("Missing database");
|
|
26
|
-
if (!
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const userProfileStoreFactory = ({ uid }) => eventStore(database, populateParameters({ uid }, config.user.profile.path));
|
|
26
|
+
if (!metaURL) throw new Error("Missing metaURL");
|
|
27
|
+
if (!authentication) throw new Error("Missing authentication");
|
|
28
|
+
|
|
29
|
+
const meta = await fetch(metaURL).then(resp => resp.json())
|
|
30
|
+
|
|
31
|
+
const log = logger(database, authentication, meta.o11y.path);
|
|
32
|
+
const teamMembershipStoreFactory = ({ tid }) => eventStore(database, authentication, populateParameters({ tid }, meta.team.membership.path));
|
|
33
|
+
const teamDetailsStoreFactory = ({ tid }) => eventStore(database, authentication, populateParameters({ tid }, meta.team.details.path));
|
|
34
|
+
const userInvitesStoreFactory = ({ emailId }) => eventStore(database, authentication, populateParameters({ emailId }, meta.userInvites.path));
|
|
35
|
+
const userTeamsStoreFactory = ({ uid }) => eventStore(database, authentication, populateParameters({ uid }, meta.user.teams.path));
|
|
36
|
+
const userProfileStoreFactory = ({ uid }) => eventStore(database, authentication, populateParameters({ uid }, meta.user.profile.path));
|
|
38
37
|
|
|
39
38
|
return {
|
|
40
39
|
|
|
@@ -5,11 +5,13 @@ const newEventId = eventType => `${timeId()}-${eventType}`;
|
|
|
5
5
|
export const PROJECTIONS = "_p";
|
|
6
6
|
export const EVENTS = "_e";
|
|
7
7
|
|
|
8
|
-
export function eventStore(database, path) {
|
|
8
|
+
export function eventStore(database, authentication, path) {
|
|
9
9
|
|
|
10
10
|
const { db, ref, onValue, get, set } = database;
|
|
11
11
|
for (const required of ["db", "ref", "onValue", "get", "set"])
|
|
12
12
|
if (!database[required]) throw new Error(`Missing required database.${required}`);
|
|
13
|
+
const { auth } = authentication;
|
|
14
|
+
if (!auth) throw new Error("Missing required authentication.auth");
|
|
13
15
|
|
|
14
16
|
const eventsPath = `${path}/${EVENTS}`;
|
|
15
17
|
const projectionsPath = `${path}/${PROJECTIONS}`;
|
|
@@ -46,8 +48,13 @@ export function eventStore(database, path) {
|
|
|
46
48
|
async record(eventType, payload) {
|
|
47
49
|
|
|
48
50
|
const eid = newEventId(eventType);
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
+
const meta = {
|
|
52
|
+
when: new Date().toISOString(),
|
|
53
|
+
uid: authentication.auth?.currentUser?.uid
|
|
54
|
+
};
|
|
55
|
+
const data = { type: eventType, payload, meta };
|
|
56
|
+
const path = `${eventsPath}/${eid}`;
|
|
57
|
+
await set(ref(db, path), data);
|
|
51
58
|
|
|
52
59
|
},
|
|
53
60
|
|
|
@@ -11,22 +11,16 @@ function munge(attributes) {
|
|
|
11
11
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function logger(database, path, defaultAttributes) {
|
|
14
|
+
export function logger(database, authentication, path, defaultAttributes) {
|
|
15
15
|
|
|
16
16
|
const { db, ref, set } = database;
|
|
17
|
-
const record = data => {
|
|
18
|
-
|
|
19
|
-
const refPath = path.replace("$id", timeId());
|
|
20
|
-
console.log("Logging", data, "to", refPath);
|
|
21
|
-
return set(ref(db, refPath), data);
|
|
22
|
-
|
|
23
|
-
};
|
|
24
17
|
|
|
25
18
|
async function recordLog(trace_id, level, sharedAttributes, { body, parent_id, attributes }) {
|
|
26
19
|
|
|
27
20
|
const span_id = generateSpanId();
|
|
28
21
|
console.log(defaultAttributes, sharedAttributes, attributes);
|
|
29
22
|
const data = {
|
|
23
|
+
meta: { uid: authentication.auth.currentUser.uid },
|
|
30
24
|
body,
|
|
31
25
|
trace_id,
|
|
32
26
|
span_id,
|
|
@@ -35,7 +29,10 @@ export function logger(database, path, defaultAttributes) {
|
|
|
35
29
|
attributes: munge({ ...defaultAttributes, ...sharedAttributes, ...attributes })
|
|
36
30
|
};
|
|
37
31
|
if (parent_id) data.parent_id = parent_id;
|
|
38
|
-
|
|
32
|
+
const refPath = path.replace("$id", timeId());
|
|
33
|
+
console.log("Logging", data, "to", refPath);
|
|
34
|
+
return await set(ref(db, refPath), data);
|
|
35
|
+
|
|
39
36
|
|
|
40
37
|
}
|
|
41
38
|
|
package/package.json
CHANGED
package/src/client/context.js
CHANGED
|
@@ -4,6 +4,7 @@ import { user } from "../../dist/browser/client/team/user.js";
|
|
|
4
4
|
|
|
5
5
|
export function inject({
|
|
6
6
|
db,
|
|
7
|
+
uid: authorUid,
|
|
7
8
|
teamMembershipConfig,
|
|
8
9
|
userTeamsConfig,
|
|
9
10
|
userInvitesConfig,
|
|
@@ -16,14 +17,15 @@ export function inject({
|
|
|
16
17
|
if (!userInvitesConfig) throw new Error("Missing: userInvitesConfig");
|
|
17
18
|
if (!teamDetailsConfig) throw new Error("Missing: teamDetailsConfig");
|
|
18
19
|
if (!userProfileConfig) throw new Error("Missing: userProfileConfig");
|
|
19
|
-
if (!db) throw new Error("Missing: db");
|
|
20
|
+
if (!db || (typeof db !== "object")) throw new Error("Missing object: db");
|
|
21
|
+
if (!authorUid || (typeof authorUid !== "string")) throw new Error("Missing string: uid");
|
|
20
22
|
|
|
21
23
|
return {
|
|
22
24
|
|
|
23
25
|
team({ tid }) {
|
|
24
26
|
|
|
25
|
-
const teamMembershipStore = eventStoreFromConfig(db, teamMembershipConfig, { tid });
|
|
26
|
-
const teamDetailsStore = eventStoreFromConfig(db, teamDetailsConfig, { tid });
|
|
27
|
+
const teamMembershipStore = eventStoreFromConfig(db, authorUid, teamMembershipConfig, { tid });
|
|
28
|
+
const teamDetailsStore = eventStoreFromConfig(db, authorUid, teamDetailsConfig, { tid });
|
|
27
29
|
return team({
|
|
28
30
|
teamMembershipStore,
|
|
29
31
|
teamDetailsStore,
|
|
@@ -35,8 +37,8 @@ export function inject({
|
|
|
35
37
|
|
|
36
38
|
userProfile({ uid }) {
|
|
37
39
|
|
|
38
|
-
const userTeamsStore = eventStoreFromConfig(db, userTeamsConfig, { uid });
|
|
39
|
-
const userProfileStore = eventStoreFromConfig(db, userProfileConfig, { uid });
|
|
40
|
+
const userTeamsStore = eventStoreFromConfig(db, authorUid, userTeamsConfig, { uid });
|
|
41
|
+
const userProfileStore = eventStoreFromConfig(db, authorUid, userProfileConfig, { uid });
|
|
40
42
|
return user({ userTeamsStore, userProfileStore });
|
|
41
43
|
|
|
42
44
|
}
|
|
@@ -45,13 +47,13 @@ export function inject({
|
|
|
45
47
|
|
|
46
48
|
function userTeamsStoreFactory({ uid }) {
|
|
47
49
|
|
|
48
|
-
return eventStoreFromConfig(db, userTeamsConfig, { uid });
|
|
50
|
+
return eventStoreFromConfig(db, authorUid, userTeamsConfig, { uid });
|
|
49
51
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
function userInvitesStoreFactory({ emailId }) {
|
|
53
55
|
|
|
54
|
-
return eventStoreFromConfig(db, userInvitesConfig, { emailId });
|
|
56
|
+
return eventStoreFromConfig(db, authorUid, userInvitesConfig, { emailId });
|
|
55
57
|
|
|
56
58
|
}
|
|
57
59
|
|
package/src/client/node.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EVENT_STORE_META } from "../event-store/configuration.js";
|
|
2
2
|
import { eventStore as eventStoreModular } from "../../dist/browser/client/event-store.js";
|
|
3
3
|
|
|
4
|
-
function
|
|
4
|
+
function adaptDatabase(db) {
|
|
5
5
|
|
|
6
6
|
return {
|
|
7
7
|
db,
|
|
@@ -42,16 +42,33 @@ function resolvePath(pathTemplate, pathValues) {
|
|
|
42
42
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export function eventStoreFromConfig(db, config, pathValues = {}) {
|
|
45
|
+
export function eventStoreFromConfig(db, uid, config, pathValues = {}) {
|
|
46
|
+
|
|
47
|
+
if (!db || typeof db !== "object") throw new Error(`Expected object for db: ${db}`);
|
|
48
|
+
if (!uid || typeof uid !== "string") throw new Error(`Expected string for uid: ${uid}`);
|
|
49
|
+
if (!config || !(EVENT_STORE_META in config)) throw new Error(`Expected config tagged with event store metadata: ${config}`);
|
|
46
50
|
|
|
47
51
|
const meta = config[EVENT_STORE_META];
|
|
48
|
-
return eventStore(db, resolvePath(meta.ref, pathValues));
|
|
52
|
+
return eventStore(db, uid, resolvePath(meta.ref, pathValues));
|
|
49
53
|
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
export function eventStore(db, path) {
|
|
56
|
+
export function eventStore(db, uid, path) {
|
|
57
|
+
|
|
58
|
+
if (!db || typeof db !== "object") throw new Error(`Expected object for db: ${db}`);
|
|
59
|
+
if (!uid || typeof uid !== "string") throw new Error(`Expected string for uid: ${uid}`);
|
|
60
|
+
if (!path || typeof path !== "string") throw new Error(`Expected string for path: ${path}`);
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
return eventStoreModular(
|
|
63
|
+
adaptDatabase(db),
|
|
64
|
+
adaptAuthentication(uid),
|
|
65
|
+
path
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
}
|
|
56
69
|
|
|
57
|
-
|
|
70
|
+
function adaptAuthentication(uid) {
|
|
71
|
+
|
|
72
|
+
return { auth: { currentUser: { uid } } };
|
|
73
|
+
|
|
74
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { EVENTS, PROJECTIONS } from "../../dist/browser/client/event-store.js";
|
|
2
2
|
import { explodePathVars } from "../index.js";
|
|
3
|
+
import { writeFileSync } from "node:fs";
|
|
3
4
|
|
|
4
5
|
function writeConditions(config) {
|
|
5
6
|
|
|
6
7
|
if (config.write === false) return false;
|
|
7
|
-
let expr = "auth.uid
|
|
8
|
+
let expr = "(auth.uid!=null) && !data.exists() && (newData.child('meta/uid').val()==auth.uid)";
|
|
8
9
|
if (config?.write)
|
|
9
10
|
expr += " && (" + explodePathVars(config.write) + ")"
|
|
10
11
|
return expr;
|
|
@@ -93,18 +94,29 @@ const parseConfig = ([_name, config]) => [
|
|
|
93
94
|
config
|
|
94
95
|
];
|
|
95
96
|
|
|
96
|
-
export function generateRules(
|
|
97
|
+
export function generateRules(argv, eventStores, rawStores) {
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
|
|
100
|
+
const rulesJSON = JSON.stringify(
|
|
100
101
|
{
|
|
101
102
|
rules: [
|
|
102
103
|
...eventStores.map(parseConfig),
|
|
103
104
|
...rawStores.map(parseConfig)
|
|
104
105
|
].reduce(addRulesForPath, {})
|
|
105
106
|
},
|
|
106
|
-
null,
|
|
107
|
+
null,
|
|
108
|
+
4
|
|
107
109
|
);
|
|
110
|
+
if (argv.output) {
|
|
111
|
+
|
|
112
|
+
console.log("Writing to", argv.output);
|
|
113
|
+
writeFileSync(argv.output, rulesJSON);
|
|
114
|
+
|
|
115
|
+
} else {
|
|
116
|
+
|
|
117
|
+
console.log(rulesJSON);
|
|
118
|
+
|
|
119
|
+
}
|
|
108
120
|
|
|
109
121
|
}
|
|
110
122
|
|
package/src/configuration.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { loadConfigurationFromModule } from "./load-configuration.js";
|
|
1
2
|
import { refPathToTriggerPath } from "./paths.js";
|
|
2
3
|
import { all, assertNewDataDoesNotHave, assertNewDataHasString, any, assertNewDataHasNumber, assertNewDataHasOneOf, assertNewDataHasLowercaseHexCharacters, assertNewDataFieldDoesNotMatch } from "./rules.js";
|
|
3
4
|
|
|
@@ -17,21 +18,72 @@ export function configureConfigStore(paths, _onValueWritten, _observe) {
|
|
|
17
18
|
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
function buildConfigDigest(module, paths) {
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const refConfigMap =
|
|
25
|
+
loadConfigurationFromModule(module)
|
|
26
|
+
.flatMap(x => x.map(y => y[1]))
|
|
27
|
+
.reduce((index, config) => ({ ...index, [config.ref]: config }), {});
|
|
28
|
+
|
|
29
|
+
function patchForPath(map) {
|
|
30
|
+
|
|
31
|
+
const ret = {
|
|
32
|
+
...map
|
|
33
|
+
};
|
|
34
|
+
const mappedConfig = refConfigMap[map.path];
|
|
35
|
+
for (const [x, y] of Object.entries(ret).filter(([_, v]) => typeof v === "object")) {
|
|
36
|
+
|
|
37
|
+
ret[x] = patchForPath(y);
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
if (mappedConfig && (mappedConfig.read || mappedConfig.write)) {
|
|
41
|
+
|
|
42
|
+
ret.projections = mappedConfig.projections;
|
|
43
|
+
ret.eventTypes = mappedConfig.eventTypes;
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
return ret;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return patchForPath(paths);
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const publicFor1Day = "public, max-age=86400";
|
|
55
|
+
|
|
56
|
+
export function configureMetadataEndpoint({ module, paths, onRequest, region, cacheControl = publicFor1Day }) {
|
|
57
|
+
|
|
58
|
+
const digest = buildConfigDigest(module, paths);
|
|
59
|
+
return onRequest(
|
|
60
|
+
{ region },
|
|
61
|
+
async (_, res) => {
|
|
62
|
+
|
|
63
|
+
res.header("Cache-Control", cacheControl).send(digest);
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
20
70
|
const googleLogLevels = ["DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY"];
|
|
21
71
|
|
|
22
72
|
function decideSeverity(newData) {
|
|
23
73
|
|
|
24
74
|
const text = newData.severity_text?.toUpperCase();
|
|
25
|
-
return googleLogLevels.includes(text) ? text : "
|
|
75
|
+
return googleLogLevels.includes(text) ? text : "INFO";
|
|
26
76
|
|
|
27
77
|
}
|
|
28
78
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return newData?.body || "Log message without body";
|
|
79
|
+
/*
|
|
32
80
|
|
|
33
|
-
|
|
81
|
+
message, severity, // required
|
|
82
|
+
traceId, spanId, timestamp, // advised
|
|
83
|
+
logData, // optional log line
|
|
84
|
+
spanStart, spanEnd, spanAttributes // optional trace attributes
|
|
34
85
|
|
|
86
|
+
*/
|
|
35
87
|
function handleO11yEventWritten(observe, e) {
|
|
36
88
|
|
|
37
89
|
const { after } = e.data;
|
|
@@ -41,16 +93,21 @@ function handleO11yEventWritten(observe, e) {
|
|
|
41
93
|
const now = new Date();
|
|
42
94
|
const time_server_ms = now.valueOf();
|
|
43
95
|
const timestamp = now.toISOString();
|
|
96
|
+
const message = newData?.message || "Missing log message";
|
|
97
|
+
|
|
98
|
+
const { traceId, spanId, ...rest } = newData
|
|
99
|
+
const logData = { ...rest, time_server_ms };
|
|
100
|
+
|
|
44
101
|
observe({
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
102
|
+
message,
|
|
103
|
+
severity: decideSeverity(newData),
|
|
104
|
+
|
|
105
|
+
traceId: newData.trace_id,
|
|
106
|
+
spanId: newData.span_id,
|
|
107
|
+
timestamp,
|
|
108
|
+
|
|
109
|
+
logData
|
|
110
|
+
|
|
54
111
|
});
|
|
55
112
|
after.ref.update({ time_server_ms });
|
|
56
113
|
|
|
@@ -6,8 +6,8 @@ export const EVENT_STORE_META = Symbol("Event store configuration metadata");
|
|
|
6
6
|
|
|
7
7
|
export function configureEventStore({ ref, projections, ...rest }, { onValueWritten, observe, region, instance }) {
|
|
8
8
|
|
|
9
|
-
if (
|
|
10
|
-
if (
|
|
9
|
+
if (typeof onValueWritten !== "function") throw new Error(`Missing onValueWritten function: ${onValueWritten}`);
|
|
10
|
+
if (typeof observe !== "function") throw new Error(`Missing observe function: ${observe}`);
|
|
11
11
|
if (!region) throw new Error("Missing region");
|
|
12
12
|
if (!instance) throw new Error("Missing instance");
|
|
13
13
|
|
package/src/exec-eventstore.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { listEventStores } from "./commands/list-event-stores.js";
|
|
4
4
|
import { generateRules } from "./commands/generate-rules.js";
|
|
@@ -35,38 +35,6 @@ main({
|
|
|
35
35
|
strategy: generateRules
|
|
36
36
|
|
|
37
37
|
},
|
|
38
|
-
// "vendor-eventstore": {
|
|
39
|
-
|
|
40
|
-
// description: "Copy event store client into your browser assets folder",
|
|
41
|
-
// loadConfig: false,
|
|
42
|
-
// parameters: {},
|
|
43
|
-
// strategy: vendor.eventStore
|
|
44
|
-
|
|
45
|
-
// },
|
|
46
|
-
// "vendor-web": {
|
|
47
|
-
|
|
48
|
-
// description: "Copy web logic platform into your browser assets folder",
|
|
49
|
-
// loadConfig: false,
|
|
50
|
-
// parameters: {},
|
|
51
|
-
// strategy: vendor.webPlatform
|
|
52
|
-
|
|
53
|
-
// },
|
|
54
|
-
// "vendor-webui": {
|
|
55
|
-
|
|
56
|
-
// description: "Copy web UI platform into your browser assets folder (requires web)",
|
|
57
|
-
// loadConfig: false,
|
|
58
|
-
// parameters: {},
|
|
59
|
-
// strategy: vendor.webPlatformUI
|
|
60
|
-
|
|
61
|
-
// },
|
|
62
|
-
// "vendor-membership": {
|
|
63
|
-
|
|
64
|
-
// description: "Copy team management logic into your browser assets folder",
|
|
65
|
-
// loadConfig: false,
|
|
66
|
-
// parameters: {},
|
|
67
|
-
// strategy: vendor.membership
|
|
68
|
-
|
|
69
|
-
// },
|
|
70
38
|
"vendor-link": {
|
|
71
39
|
|
|
72
40
|
description: "Symlink latest vendored h1v3 assets as h1v3@latest",
|
package/src/index.js
CHANGED
|
@@ -9,11 +9,15 @@ export async function loadConfiguration(argv) {
|
|
|
9
9
|
if (!argv.snippet)
|
|
10
10
|
console.log("Loading configuration:", configPath);
|
|
11
11
|
const module = await import(pathToFileURL(configPath));
|
|
12
|
+
return loadConfigurationFromModule(module)
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function loadConfigurationFromModule(module) {
|
|
12
17
|
return [
|
|
13
18
|
readStoresMetadata(module),
|
|
14
19
|
readRawMetadata(module)
|
|
15
|
-
]
|
|
16
|
-
|
|
20
|
+
];
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export function readRawMetadata(config) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export function provider({ trace, projectId, serviceName, log }) {
|
|
2
2
|
|
|
3
3
|
if (!serviceName) throw new Error("Missing serviceName");
|
|
4
4
|
if (!trace) throw new Error("Missing trace");
|
|
@@ -44,7 +44,7 @@ function generateLowerHex(byteLength) {
|
|
|
44
44
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
function toTraceAttributes(obj) {
|
|
48
48
|
|
|
49
49
|
const attributeMap = {};
|
|
50
50
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -81,8 +81,6 @@ async function writeLogLine({ log, projectId }, { message, tracePath, spanId, se
|
|
|
81
81
|
traceSampled: "true",
|
|
82
82
|
timestamp
|
|
83
83
|
};
|
|
84
|
-
console.log("Log meta", logMeta);
|
|
85
|
-
|
|
86
84
|
const entry = log.entry(
|
|
87
85
|
logMeta,
|
|
88
86
|
{
|