h1v3 0.3.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +3 -0
- package/dist/browser/client/auth.js +128 -0
- package/dist/browser/client/bus.js +1 -0
- package/dist/browser/{web-ui → client}/components/login.js +11 -2
- package/dist/browser/client/components/notification.html.js +25 -0
- package/dist/browser/client/components/notification.js +27 -0
- package/{src/client/web-ui → dist/browser/client}/components/partials/wa-utils.js +1 -0
- package/dist/browser/client/context.js +98 -0
- package/dist/browser/{event-store/modular.js → client/event-store.js} +21 -14
- package/{src/client/web → dist/browser/client}/events.js +5 -1
- package/dist/browser/{web/system.js → client/firebase.js} +1 -7
- package/dist/browser/client/id.js +37 -0
- package/dist/browser/client/logger.js +60 -0
- package/dist/browser/client/notifications.js +50 -0
- package/{src/client/web-ui → dist/browser/client}/system.js +10 -4
- package/dist/browser/client/team/invites.js +119 -0
- package/dist/browser/client/team/team.js +143 -0
- package/dist/browser/client/team/user.js +62 -0
- package/package.json +2 -7
- package/src/client/context.js +58 -0
- package/src/client/index.js +2 -2
- package/src/client/node.js +29 -1
- package/src/commands/generate-rules.js +56 -26
- package/src/commands/vendor.js +90 -21
- package/src/configuration.js +106 -0
- package/src/event-store/{initialise.js → configuration.js} +15 -4
- package/src/event-store/projections.js +40 -11
- package/src/exec-eventstore.js +52 -10
- package/src/index.js +20 -4
- package/src/load-configuration.js +17 -3
- package/src/membership/index.js +5 -2
- package/src/membership/team-details/events.js +5 -0
- package/src/membership/team-details/projections/current.js +9 -0
- package/src/membership/team-details/store.js +30 -0
- package/src/membership/team-details/verify-store-paths.js +8 -0
- package/src/membership/team-membership/events.js +17 -0
- package/src/membership/team-membership/projections/_shared.js +55 -0
- package/src/membership/team-membership/projections/details.js +105 -0
- package/src/membership/team-membership/projections/inviteToEmail.js +25 -0
- package/src/membership/team-membership/projections/members.js +68 -0
- package/src/membership/team-membership/store.js +56 -0
- package/src/membership/user-profile/events.js +6 -0
- package/src/membership/user-profile/projections/current.js +9 -0
- package/src/membership/user-profile/store.js +28 -0
- package/src/membership/user-teams/events.js +5 -0
- package/src/membership/user-teams/projections/current.js +21 -0
- package/src/membership/user-teams/store.js +27 -0
- package/src/membership/userInvites/events.js +12 -0
- package/src/membership/userInvites/projections/pending.js +45 -0
- package/src/membership/userInvites/store.js +46 -0
- package/src/paths.js +5 -0
- package/src/rules.js +153 -0
- package/src/schema.js +91 -1
- package/src/system/main.js +1 -1
- package/dist/browser/web/events.js +0 -7
- package/dist/browser/web/login.js +0 -48
- package/dist/browser/web-ui/components/notification.html.js +0 -12
- package/dist/browser/web-ui/components/notification.js +0 -25
- package/dist/browser/web-ui/components/partials/wa-utils.js +0 -17
- package/dist/browser/web-ui/errors.js +0 -23
- package/dist/browser/web-ui/system.js +0 -20
- package/scripts/dist-client.js +0 -31
- package/src/client/modular.js +0 -82
- package/src/client/web/login.js +0 -48
- package/src/client/web/system.js +0 -67
- package/src/client/web-ui/components/login.html.js +0 -44
- package/src/client/web-ui/components/login.js +0 -74
- package/src/client/web-ui/components/notification.html.js +0 -12
- package/src/client/web-ui/components/notification.js +0 -25
- package/src/client/web-ui/errors.js +0 -23
- package/src/membership/membership-store.js +0 -88
- package/src/membership/user-profile-store.js +0 -39
- /package/dist/browser/{web-ui → client}/components/login.html.js +0 -0
package/src/commands/vendor.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
|
|
5
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -12,13 +13,11 @@ async function readPackageJSON() {
|
|
|
12
13
|
|
|
13
14
|
async function vendor(subpath) {
|
|
14
15
|
|
|
15
|
-
const { version, name } = await readPackageJSON();
|
|
16
16
|
const src = join(__dirname, "..", "..", "dist", "browser", subpath);;
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
// Destination: include semver
|
|
20
|
-
const versionedName =
|
|
21
|
-
const dest =join(
|
|
19
|
+
const versionedName = await versionedPackageName();
|
|
20
|
+
const dest =join(destVendorPath(), versionedName, subpath);
|
|
22
21
|
|
|
23
22
|
// Recursive copy
|
|
24
23
|
await fs.cp(src, dest, { recursive: true });
|
|
@@ -26,29 +25,99 @@ async function vendor(subpath) {
|
|
|
26
25
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
async function versionedPackageName() {
|
|
29
|
+
|
|
30
|
+
const { version, name } = await readPackageJSON();
|
|
31
|
+
const versionedName = `${name}@${version}`;
|
|
32
|
+
return versionedName;
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function destVendorPath() {
|
|
37
|
+
|
|
38
|
+
return join(process.cwd(), "public", "vendor");
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// export async function eventStore(_argv) {
|
|
43
|
+
|
|
44
|
+
// await vendor("event-store");
|
|
45
|
+
|
|
46
|
+
// }
|
|
47
|
+
|
|
48
|
+
// export async function webPlatform(_argv) {
|
|
49
|
+
|
|
50
|
+
// await vendor("web");
|
|
51
|
+
|
|
52
|
+
// }
|
|
53
|
+
|
|
54
|
+
export async function deps(_argv) {
|
|
55
|
+
|
|
56
|
+
// lit
|
|
57
|
+
const dest = join(destVendorPath(), "lit@3.3.1", "dist");
|
|
58
|
+
const destFile = join(dest, "lit-core.min.js");
|
|
59
|
+
if (existsSync(destFile)) {
|
|
60
|
+
|
|
61
|
+
console.log("Skipping", destFile);
|
|
62
|
+
|
|
63
|
+
} else {
|
|
64
|
+
|
|
65
|
+
const resp = await fetch("https://cdn.jsdelivr.net/gh/lit/dist@3.3.1/core/lit-core.min.js")
|
|
66
|
+
const lit = await resp.text();
|
|
67
|
+
await fs.mkdir(dest, { recursive: true });
|
|
68
|
+
await fs.writeFile(destFile, lit);
|
|
69
|
+
console.log("Vendored", destFile);
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function client(_argv) {
|
|
30
76
|
|
|
31
|
-
await vendor("
|
|
77
|
+
await vendor("client");
|
|
32
78
|
|
|
33
79
|
}
|
|
34
80
|
|
|
35
|
-
export async function
|
|
81
|
+
// export async function webPlatformUI(_argv) {
|
|
36
82
|
|
|
37
|
-
|
|
83
|
+
// await webPlatform(_argv);
|
|
84
|
+
// await vendor("web-ui");
|
|
85
|
+
// const resp = await fetch("https://cdn.jsdelivr.net/gh/lit/dist@3.3.1/core/lit-core.min.js")
|
|
86
|
+
// const lit = await resp.text();
|
|
87
|
+
// const dest = join(".", "public", "vendor", "lit@3.3.1", "dist");
|
|
88
|
+
// await fs.mkdir(dest, { recursive: true });
|
|
89
|
+
// await fs.writeFile(
|
|
90
|
+
// join(".", "public", "vendor", "lit@3.3.1", "dist", "lit-core.min.js"),
|
|
91
|
+
// lit
|
|
92
|
+
// );
|
|
93
|
+
|
|
94
|
+
// }
|
|
95
|
+
|
|
96
|
+
// export async function membership(_argv) {
|
|
97
|
+
|
|
98
|
+
// await eventStore(_argv);
|
|
99
|
+
// await vendor("team");
|
|
100
|
+
|
|
101
|
+
// }
|
|
102
|
+
|
|
103
|
+
export async function symLinkLatest(_argv) {
|
|
104
|
+
|
|
105
|
+
const vendorPath = destVendorPath();
|
|
106
|
+
const versionedName = await versionedPackageName();
|
|
107
|
+
|
|
108
|
+
const path = join(vendorPath, "h1v3@latest");
|
|
109
|
+
const target = join(vendorPath, versionedName);
|
|
110
|
+
await fs.unlink(path);
|
|
111
|
+
await fs.symlink(versionedName, path, "dir");
|
|
112
|
+
|
|
113
|
+
console.log("Symlinked", versionedName, "to", path);
|
|
38
114
|
|
|
39
115
|
}
|
|
40
116
|
|
|
41
|
-
export async function
|
|
42
|
-
|
|
43
|
-
await
|
|
44
|
-
await
|
|
45
|
-
|
|
46
|
-
const lit = await resp.text();
|
|
47
|
-
const dest = join(".", "public", "vendor", "lit@3.3.1", "dist");
|
|
48
|
-
await fs.mkdir(dest, { recursive: true });
|
|
49
|
-
await fs.writeFile(
|
|
50
|
-
join(".", "public", "vendor", "lit@3.3.1", "dist", "lit-core.min.js"),
|
|
51
|
-
lit
|
|
52
|
-
);
|
|
117
|
+
export async function all(argv) {
|
|
118
|
+
|
|
119
|
+
await deps(argv);
|
|
120
|
+
await client(argv);
|
|
121
|
+
await symLinkLatest(argv)
|
|
53
122
|
|
|
54
123
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { refPathToTriggerPath } from "./paths.js";
|
|
2
|
+
import { all, assertNewDataDoesNotHave, assertNewDataHasString, any, assertNewDataHasNumber, assertNewDataHasOneOf, assertNewDataHasLowercaseHexCharacters, assertNewDataFieldDoesNotMatch } from "./rules.js";
|
|
3
|
+
|
|
4
|
+
export const RAW_STORE_META = Symbol("Raw database metadata");
|
|
5
|
+
|
|
6
|
+
export function configureConfigStore(paths, _onValueWritten, _logger) {
|
|
7
|
+
|
|
8
|
+
if (!paths?.config?.path) throw new Error("Missing: paths.config.path");
|
|
9
|
+
return {
|
|
10
|
+
[RAW_STORE_META]: {
|
|
11
|
+
raw: true,
|
|
12
|
+
ref: paths.config.path,
|
|
13
|
+
read: true,
|
|
14
|
+
write: false
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function decideSeverity(newData) {
|
|
21
|
+
|
|
22
|
+
if (!newData.severity_text) return "INFO";
|
|
23
|
+
return newData.severity_text.toUpperCase();
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function decideMessage(newData) {
|
|
28
|
+
|
|
29
|
+
return newData?.body || "Log message without body";
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function handleLogValueWritten(logger, e) {
|
|
34
|
+
|
|
35
|
+
const { after } = e.data;
|
|
36
|
+
const newData = after.val();
|
|
37
|
+
if (!newData.time_server_ms) {
|
|
38
|
+
|
|
39
|
+
const now = new Date();
|
|
40
|
+
const time_server_ms = now.valueOf();
|
|
41
|
+
const timestamp = now.toISOString();
|
|
42
|
+
const payload = {
|
|
43
|
+
"logging.googleapis.com/trace": `projects/${process.env.GCLOUD_PROJECT}/traces/${newData.trace_id}`,
|
|
44
|
+
"logging.googleapis.com/spanId": newData.span_id,
|
|
45
|
+
"message": decideMessage(newData),
|
|
46
|
+
"severity": decideSeverity(newData),
|
|
47
|
+
"raw": {
|
|
48
|
+
...newData,
|
|
49
|
+
time_server_ms
|
|
50
|
+
},
|
|
51
|
+
timestamp
|
|
52
|
+
};
|
|
53
|
+
console.log("Writing log", payload);
|
|
54
|
+
logger.write(payload);
|
|
55
|
+
after.ref.update({ time_server_ms });
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function configureLoggingStore(paths, onValueWritten, logger) {
|
|
62
|
+
|
|
63
|
+
const ref = paths?.logs?.path;
|
|
64
|
+
if (!ref) throw new Error("Missing: paths.logs.path");
|
|
65
|
+
const triggerPath = refPathToTriggerPath(ref);
|
|
66
|
+
return Object.assign(
|
|
67
|
+
onValueWritten(triggerPath, handleLogValueWritten.bind(this, logger)),
|
|
68
|
+
{
|
|
69
|
+
[RAW_STORE_META]: {
|
|
70
|
+
raw: true,
|
|
71
|
+
ref,
|
|
72
|
+
read: false,
|
|
73
|
+
write: all(
|
|
74
|
+
// required
|
|
75
|
+
assertNewDataHasString("body", 500, 1),
|
|
76
|
+
assertNewDataHasOneOf("severity_text", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"),
|
|
77
|
+
// trace context
|
|
78
|
+
all(
|
|
79
|
+
assertNewDataHasLowercaseHexCharacters("trace_id", 32),
|
|
80
|
+
assertNewDataFieldDoesNotMatch("trace_id", "0*")
|
|
81
|
+
),
|
|
82
|
+
all(
|
|
83
|
+
assertNewDataHasLowercaseHexCharacters("span_id", 16),
|
|
84
|
+
assertNewDataFieldDoesNotMatch("span_id", "0*")
|
|
85
|
+
),
|
|
86
|
+
// forbid server time faking
|
|
87
|
+
assertNewDataDoesNotHave("time_server_ms"),
|
|
88
|
+
// optional bits
|
|
89
|
+
any(
|
|
90
|
+
assertNewDataDoesNotHave("time_client_ms"),
|
|
91
|
+
assertNewDataHasNumber("time_client_ms")
|
|
92
|
+
),
|
|
93
|
+
any(
|
|
94
|
+
assertNewDataDoesNotHave("parent_id"),
|
|
95
|
+
all(
|
|
96
|
+
assertNewDataHasLowercaseHexCharacters("parent_id", 16),
|
|
97
|
+
assertNewDataFieldDoesNotMatch("parent_id", "0*")
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
import { EVENTS } from "../../dist/browser/client/event-store.js";
|
|
1
2
|
import { updateProjections } from "./projections.js";
|
|
3
|
+
import { refPathToTriggerPath } from "../paths.js";
|
|
2
4
|
|
|
3
5
|
export const EVENT_STORE_META = Symbol("Event store configuration metadata");
|
|
4
6
|
|
|
5
|
-
export function
|
|
7
|
+
export function configureEventStore({ ref, projections, ...rest }, onValueWritten, logger) {
|
|
6
8
|
|
|
7
9
|
// considered the "home" of the event store, under which events and projections will be stored
|
|
8
|
-
const eventStorePath = ref
|
|
10
|
+
const eventStorePath = refPathToTriggerPath(ref);
|
|
9
11
|
|
|
10
12
|
// the path which will trigger event store projections
|
|
11
|
-
const writeRefPath = `${eventStorePath}/
|
|
13
|
+
const writeRefPath = `${eventStorePath}/${EVENTS}/{eid}`;
|
|
12
14
|
|
|
13
15
|
// handle an incoming write event
|
|
14
|
-
const handler = e => updateProjections(e.data.after, projections, logger);
|
|
16
|
+
const handler = e => updateProjections(e.data.after, projections, logger, rest.debug);
|
|
15
17
|
|
|
16
18
|
const metadata = {
|
|
17
19
|
...rest,
|
|
@@ -19,6 +21,7 @@ export function configure({ ref, projections, ...rest }, onValueWritten, logger)
|
|
|
19
21
|
triggerPath: writeRefPath,
|
|
20
22
|
projections: Object.keys(projections)
|
|
21
23
|
};
|
|
24
|
+
|
|
22
25
|
// tag the trigger with metadata
|
|
23
26
|
return Object.assign(
|
|
24
27
|
onValueWritten(writeRefPath, handler),
|
|
@@ -26,3 +29,11 @@ export function configure({ ref, projections, ...rest }, onValueWritten, logger)
|
|
|
26
29
|
);
|
|
27
30
|
|
|
28
31
|
}
|
|
32
|
+
|
|
33
|
+
configureEventStore.inject = ({ onValueWritten, logger, paths }) =>
|
|
34
|
+
|
|
35
|
+
(eventStoreFactory, ...args) =>
|
|
36
|
+
|
|
37
|
+
configureEventStore(eventStoreFactory(paths, ...args), onValueWritten, logger);
|
|
38
|
+
|
|
39
|
+
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { PROJECTIONS } from "../../dist/browser/client/event-store.js";
|
|
2
|
+
import { inspect } from "node:util";
|
|
3
|
+
|
|
4
|
+
export async function updateProjections(incomingEventSnap, projectionTransformationPerEvent, logger, debug = false) {
|
|
2
5
|
|
|
3
6
|
// order the events
|
|
4
7
|
const eventsSnap = await incomingEventSnap.ref.parent.get();
|
|
@@ -9,29 +12,55 @@ export async function updateProjections(incomingEventSnap, projectionTransformat
|
|
|
9
12
|
for(const [key, transformations] of projections) {
|
|
10
13
|
|
|
11
14
|
const fallbackTransformation = transformations["?"];
|
|
15
|
+
const trace = [key];
|
|
16
|
+
const context = {
|
|
17
|
+
projection: key
|
|
18
|
+
};
|
|
12
19
|
const view = sortedEvents.reduce(
|
|
13
|
-
(agg,
|
|
20
|
+
(agg, [eventKey, eventValue]) => {
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
context.eventId = eventKey;
|
|
23
|
+
if (debug) trace.push(agg, e);
|
|
24
|
+
const transform = transformations[eventValue?.type] || fallbackTransformation || missingTransformFor(key);
|
|
25
|
+
return transform(agg, eventValue, context);
|
|
17
26
|
|
|
18
27
|
}, {});
|
|
28
|
+
|
|
29
|
+
if (debug) console.debug(...trace, view);
|
|
19
30
|
await writeProjection(incomingEventSnap, key, view);
|
|
20
31
|
|
|
21
32
|
}
|
|
22
33
|
|
|
23
|
-
function
|
|
34
|
+
function missingTransformFor(projection) {
|
|
35
|
+
|
|
36
|
+
return (agg, e) => {
|
|
37
|
+
|
|
38
|
+
logger.warn("Missing transform for event", { projection, event: e });
|
|
39
|
+
return agg;
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
return agg;
|
|
41
|
+
};
|
|
27
42
|
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
async function writeProjection(incomingEventSnap,
|
|
45
|
+
async function writeProjection(incomingEventSnap, projection, view) {
|
|
46
|
+
|
|
47
|
+
const projectionRef = incomingEventSnap.ref.parent.parent.child(PROJECTIONS).child(projection);
|
|
48
|
+
const incomingEvent = incomingEventSnap.val();
|
|
49
|
+
logger.info("Writing projection after event", {
|
|
50
|
+
path: projectionRef,
|
|
51
|
+
incomingEvent
|
|
52
|
+
});
|
|
53
|
+
try {
|
|
54
|
+
|
|
55
|
+
await projectionRef.set(view);
|
|
56
|
+
|
|
57
|
+
} catch(err) {
|
|
58
|
+
|
|
59
|
+
const data = inspect(view).replace(/\n */g, " ");
|
|
60
|
+
const path = projectionRef.toString();
|
|
61
|
+
logger.error(`Error writing projection: ${err?.message}`, { projection, path, data });
|
|
31
62
|
|
|
32
|
-
|
|
33
|
-
logger.info("Writing projection after event", { path: projectionRef, incomingEvent: incomingEventSnap.val() });
|
|
34
|
-
await projectionRef.set(view);
|
|
63
|
+
}
|
|
35
64
|
|
|
36
65
|
}
|
|
37
66
|
|
package/src/exec-eventstore.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { listEventStores } from "./commands/list-event-stores.js";
|
|
4
4
|
import { generateRules } from "./commands/generate-rules.js";
|
|
5
5
|
import { main } from "./system/main.js";
|
|
6
|
-
import
|
|
6
|
+
import * as vendor from "./commands/vendor.js";
|
|
7
7
|
|
|
8
8
|
const configParameter = {
|
|
9
9
|
|
|
@@ -35,29 +35,71 @@ main({
|
|
|
35
35
|
strategy: generateRules
|
|
36
36
|
|
|
37
37
|
},
|
|
38
|
-
"vendor-eventstore": {
|
|
38
|
+
// "vendor-eventstore": {
|
|
39
39
|
|
|
40
|
-
|
|
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
|
+
"vendor-link": {
|
|
71
|
+
|
|
72
|
+
description: "Symlink latest vendored h1v3 assets as h1v3@latest",
|
|
41
73
|
loadConfig: false,
|
|
42
74
|
parameters: {},
|
|
43
|
-
strategy:
|
|
75
|
+
strategy: vendor.symLinkLatest
|
|
44
76
|
|
|
45
77
|
},
|
|
46
|
-
"vendor-web": {
|
|
47
78
|
|
|
48
|
-
|
|
79
|
+
"vendor-client": {
|
|
80
|
+
|
|
81
|
+
description: "Copy all h1v3 logic into your browser assets folder",
|
|
49
82
|
loadConfig: false,
|
|
50
83
|
parameters: {},
|
|
51
|
-
strategy:
|
|
84
|
+
strategy: vendor.client
|
|
52
85
|
|
|
53
86
|
},
|
|
54
|
-
"vendor-webui": {
|
|
55
87
|
|
|
56
|
-
|
|
88
|
+
"vendor-deps": {
|
|
89
|
+
|
|
90
|
+
description: "Copy all external dependencies for h1v3 into your browser assets folder",
|
|
57
91
|
loadConfig: false,
|
|
58
92
|
parameters: {},
|
|
59
|
-
strategy:
|
|
93
|
+
strategy: vendor.deps
|
|
94
|
+
|
|
95
|
+
},
|
|
60
96
|
|
|
97
|
+
"vendor-all": {
|
|
98
|
+
|
|
99
|
+
description: "Copy all h1v3 logic into your browser assets folder",
|
|
100
|
+
loadConfig: false,
|
|
101
|
+
parameters: {},
|
|
102
|
+
strategy: vendor.all
|
|
61
103
|
}
|
|
62
104
|
|
|
63
105
|
});
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { PROJECTIONS, EVENTS } from "../dist/browser/client/event-store.js";
|
|
2
|
+
|
|
3
|
+
export * from "./event-store/configuration.js";
|
|
4
|
+
|
|
5
|
+
export * from "./configuration.js";
|
|
2
6
|
|
|
3
7
|
export const passThroughView = {
|
|
4
8
|
"?": (view, e, key) => {
|
|
@@ -10,9 +14,21 @@ export const passThroughView = {
|
|
|
10
14
|
}
|
|
11
15
|
};
|
|
12
16
|
|
|
13
|
-
export
|
|
17
|
+
export * as userProfile from "./membership/user-profile/store.js";
|
|
18
|
+
|
|
19
|
+
export * as teamMembership from "./membership/team-membership/store.js";
|
|
20
|
+
|
|
21
|
+
export * as teamDetails from "./membership/team-details/store.js";
|
|
22
|
+
|
|
23
|
+
export * as userTeams from "./membership/user-teams/store.js";
|
|
24
|
+
|
|
25
|
+
export * as userInvites from "./membership/userInvites/store.js";
|
|
26
|
+
|
|
27
|
+
export * as rules from "./rules.js";
|
|
28
|
+
|
|
29
|
+
export function explodePathVars(expr) {
|
|
14
30
|
|
|
15
|
-
|
|
16
|
-
return expr.replaceAll(
|
|
31
|
+
if (typeof expr !== "string") return expr;
|
|
32
|
+
return expr.replaceAll(/\/(\$[^/]*)/g, "/' + $1 + '");
|
|
17
33
|
|
|
18
34
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from "path";
|
|
2
2
|
import { pathToFileURL } from "url";
|
|
3
|
-
import { EVENT_STORE_META } from "./event-store/
|
|
3
|
+
import { EVENT_STORE_META } from "./event-store/configuration.js";
|
|
4
|
+
import { RAW_STORE_META } from "./configuration.js";
|
|
4
5
|
|
|
5
6
|
export async function loadConfiguration(argv) {
|
|
6
7
|
|
|
@@ -8,7 +9,19 @@ export async function loadConfiguration(argv) {
|
|
|
8
9
|
if (!argv.snippet)
|
|
9
10
|
console.log("Loading configuration:", configPath);
|
|
10
11
|
const module = await import(pathToFileURL(configPath));
|
|
11
|
-
return
|
|
12
|
+
return [
|
|
13
|
+
readStoresMetadata(module),
|
|
14
|
+
readRawMetadata(module)
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function readRawMetadata(config) {
|
|
20
|
+
|
|
21
|
+
return Object.entries(config)
|
|
22
|
+
.map(([k, v]) => [k, v?.[RAW_STORE_META]])
|
|
23
|
+
.filter(x => x[1])
|
|
24
|
+
;
|
|
12
25
|
|
|
13
26
|
}
|
|
14
27
|
|
|
@@ -16,6 +29,7 @@ export function readStoresMetadata(config) {
|
|
|
16
29
|
|
|
17
30
|
return Object.entries(config)
|
|
18
31
|
.map(([k, v]) => [k, v?.[EVENT_STORE_META]])
|
|
19
|
-
.filter(x => x[1])
|
|
32
|
+
.filter(x => x[1])
|
|
33
|
+
;
|
|
20
34
|
|
|
21
35
|
}
|
package/src/membership/index.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * as userProfile from "./user-profile/store.js";
|
|
2
|
+
export * as teamMembership from "./team-membership/store.js";
|
|
3
|
+
export * as teamDetails from "./team-details/store.js";
|
|
4
|
+
export * as userTeams from "./user-teams/store.js";
|
|
5
|
+
export * as userInvites from "./userInvites/store.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { assertIsMyTeam } from "../../rules.js";
|
|
2
|
+
import { eventTypes } from "./events.js";
|
|
3
|
+
import current from "./projections/current.js";
|
|
4
|
+
import { verifyStorePaths } from "./verify-store-paths.js";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function store(paths, schema) {
|
|
8
|
+
|
|
9
|
+
verifyStorePaths(paths, {
|
|
10
|
+
team: {
|
|
11
|
+
membership: { path: String },
|
|
12
|
+
details: { path: String }
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
if (!schema) throw new Error("Missing schema");
|
|
17
|
+
return {
|
|
18
|
+
ref: paths.team.details.path,
|
|
19
|
+
projections: {
|
|
20
|
+
current: current(schema)
|
|
21
|
+
},
|
|
22
|
+
write: false,
|
|
23
|
+
read: assertIsMyTeam(paths),
|
|
24
|
+
eventTypes
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export * from "./events.js";
|
|
30
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { checkSchema } from "../../schema.js";
|
|
2
|
+
|
|
3
|
+
export function verifyStorePaths(paths, schema) {
|
|
4
|
+
|
|
5
|
+
const { valid, errors } = checkSchema(schema, paths, { allowUnknownKeys: true, requireAllKeys: true });
|
|
6
|
+
if (!valid) throw new Error(`Store paths:\n - ${errors.join("\n - ")}`);
|
|
7
|
+
|
|
8
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const BECAME_MEMBER = "BECAME_MEMBER";
|
|
2
|
+
export const BECAME_ADMIN = "BECAME_ADMIN";
|
|
3
|
+
export const LEFT_TEAM = "LEFT_TEAM";
|
|
4
|
+
export const MEMBER_INVITED = "MEMBER_INVITED";
|
|
5
|
+
export const ADMIN_INVITED = "ADMIN_INVITED";
|
|
6
|
+
export const INVITE_ACCEPTED = "INVITE_ACCEPTED";
|
|
7
|
+
export const INVITE_REJECTED = "INVITE_REJECTED";
|
|
8
|
+
|
|
9
|
+
export const eventTypes = [
|
|
10
|
+
BECAME_MEMBER,
|
|
11
|
+
BECAME_ADMIN,
|
|
12
|
+
LEFT_TEAM,
|
|
13
|
+
MEMBER_INVITED,
|
|
14
|
+
ADMIN_INVITED,
|
|
15
|
+
INVITE_ACCEPTED,
|
|
16
|
+
INVITE_REJECTED
|
|
17
|
+
];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
export function resolveInvitationView(payload, meta) {
|
|
3
|
+
|
|
4
|
+
return {
|
|
5
|
+
id: payload.id,
|
|
6
|
+
name: payload.name || "",
|
|
7
|
+
email: payload.email || "",
|
|
8
|
+
created: meta?.when || "",
|
|
9
|
+
createdBy: payload.createdBy || ""
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function resolveUserView({ name, email, createdBy }, found, meta) {
|
|
15
|
+
|
|
16
|
+
name = name || found?.name || "";
|
|
17
|
+
email = email || found?.email;
|
|
18
|
+
const view = {
|
|
19
|
+
name
|
|
20
|
+
};
|
|
21
|
+
if (meta?.when) view.updated = meta.when;
|
|
22
|
+
if (email) view.email = email;
|
|
23
|
+
if (createdBy) view.createdBy = createdBy;
|
|
24
|
+
return view;
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function purgeInvited(view, key) {
|
|
29
|
+
|
|
30
|
+
if (view.invitedMembers && (key in view.invitedMembers))
|
|
31
|
+
delete view.invitedMembers[key];
|
|
32
|
+
if (view.invitedAdmins && (key in view.invitedAdmins))
|
|
33
|
+
delete view.invitedAdmins[key];
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function purgeUser(view, { uid }) {
|
|
38
|
+
|
|
39
|
+
const found = view.members?.[uid] || view.admins?.[uid] || view.owners?.[uid];
|
|
40
|
+
if (view.members && (uid in view.members))
|
|
41
|
+
delete view.members[uid];
|
|
42
|
+
if (view.admins && (uid in view.admins))
|
|
43
|
+
delete view.admins[uid];
|
|
44
|
+
return found;
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const passThrough = x => x;
|
|
49
|
+
export function recordInviteInContext(context, id, data) {
|
|
50
|
+
|
|
51
|
+
context.invites = context.invites || {};
|
|
52
|
+
context.invites[id] = data;
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|