@tatara/cli 0.11.1 → 0.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tatara/cli",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "Create and grow opinionated Hono backends",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,7 +1,10 @@
1
- /** Built-in roles. Add a key here, then grant it permissions in module permission files. */
1
+ /**
2
+ * Built-in roles. Add a key here, then grant it permissions in module permission files. A role is
3
+ * held only by those it's assigned to, unless `heldBy` says every org member or an identity role.
4
+ */
2
5
  export const SYSTEM_ROLES = [
3
- { key: "admin", name: "Admin", description: "Full access, including roles and members." },
4
- { key: "member", name: "Member", description: "Default role for every member." },
6
+ // tatara:slot admin
7
+ { key: "member", name: "Member", description: "Every member of the org.", heldBy: "members" },
5
8
  // tatara:slot roles
6
9
  ] as const;
7
10
 
@@ -0,0 +1,26 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
+ import { authz } from "#core/authz/index";
3
+ import { api } from "#tests/helpers/api";
4
+ import { authenticate } from "#tests/helpers/auth";
5
+ import { resetDatabase } from "#tests/helpers/db";
6
+
7
+ beforeEach(async () => {
8
+ await resetDatabase();
9
+ await authz.syncSystemRoles();
10
+ });
11
+
12
+ describe("an organization's creator", () => {
13
+ it("is its admin", async () => {
14
+ const user = await authenticate();
15
+ const created = await api.post<{ id: string }>(
16
+ "/api/auth/organization/create",
17
+ { name: "New School", slug: `new-${crypto.randomUUID()}` },
18
+ { headers: user.headers },
19
+ );
20
+ expect(created.status).toBe(200);
21
+ const me = await api.get<{ data: { roles: string[] } }>("/api/authorization/me", {
22
+ headers: { ...user.headers, "x-org-id": created.body.id },
23
+ });
24
+ expect(me.body.data.roles).toContain("admin");
25
+ });
26
+ });
@@ -41,6 +41,7 @@ __PM_EXEC__ tatara make route|service|command <name> --module <module>
41
41
  __PM_EXEC__ tatara run --list # app commands
42
42
  __PM_EXEC__ tatara routes # every route and what protects it
43
43
  __PM_EXEC__ tatara upgrade # after bumping @tatara/cli: run its codemods, sync, verify
44
+ __PM_EXEC__ tatara add <add-on…> # add jobs, cache, realtime… to this app later
44
45
  ```
45
46
 
46
47
  ## HTTP
@@ -73,6 +74,8 @@ __PM_EXEC__ tatara upgrade # after bumping @tatara/cli: run its co
73
74
  Never `Math.random()` for anything a user could guess.
74
75
  - User text: `text({ max })` and `url()` from `@tatara/core` in validators clean and check it (control,
75
76
  invisible and bidi characters, whitespace; only http(s) links). Escape when you render HTML.
77
+ Rich text users write: `sanitizeHtml(html)` (or `"email"` for templates) from `@tatara/core/html`
78
+ once `sanitize-html` is a dependency.
76
79
 
77
80
  ## Deploying
78
81
 
@@ -52,6 +52,9 @@ export const { notify, consumers } = createNotifier<NotificationsOf<typeof notif
52
52
  ? { email: user.email ?? null, phone: user.phoneNumber ?? null, language: user.language ?? null }
53
53
  : null;
54
54
  },
55
+ // who gets what is yours to decide: preferences, quiet hours… ({ userId, notification, channel,
56
+ // data }); false skips that channel for that user
57
+ allow: () => true,
55
58
  sms: sms(),
56
59
  push: push(),
57
60
  // tatara:slot senders
@@ -1,11 +1,10 @@
1
1
  // Loaded before the app (`node --import ./dist/apps/instrument.mjs …`, set in the Dockerfile):
2
2
  // instrumentation can only patch libraries imported after it, so app code comes in dynamically.
3
- import { register } from "node:module";
4
-
5
3
  const { env } = await import("#core/config/env");
6
4
 
7
5
  if (env.OTEL_EXPORTER_OTLP_ENDPOINT) {
8
- register("@opentelemetry/instrumentation/hook.mjs", import.meta.url);
6
+ const { registerInstrumentationHooks } = await import("@tatara/observability/hooks");
7
+ await registerInstrumentationHooks();
9
8
  const { startTelemetry } = await import("@tatara/observability");
10
9
  startTelemetry({
11
10
  serviceName: env.OTEL_SERVICE_NAME,
@@ -31,13 +31,13 @@
31
31
  "@aws-sdk/s3-request-presigner": "3.1152.0",
32
32
  "nodemailer": "10.1.1",
33
33
  "@types/nodemailer": "8.0.2",
34
- "@tatara/core": "0.1.2",
34
+ "@tatara/core": "0.1.3",
35
35
  "@tatara/http": "0.1.2",
36
36
  "@tatara/db": "0.2.2",
37
37
  "@tatara/testing": "0.1.1",
38
- "@tatara/auth": "0.2.0",
38
+ "@tatara/auth": "0.2.1",
39
39
  "@tatara/keycloak": "0.1.2",
40
- "@tatara/authz": "0.3.1",
40
+ "@tatara/authz": "0.5.0",
41
41
  "@tatara/storage": "0.2.0",
42
42
  "@tatara/email": "0.3.0",
43
43
  "@tatara/i18n": "0.1.0",
@@ -45,13 +45,13 @@
45
45
  "@tatara/cache": "0.1.0",
46
46
  "openapi-typescript": "7.13.0",
47
47
  "kafkajs": "2.2.4",
48
- "@tatara/notifications": "0.1.0",
48
+ "@tatara/notifications": "0.2.0",
49
49
  "@tatara/realtime": "0.1.0",
50
50
  "socket.io": "4.8.4",
51
51
  "socket.io-client": "4.8.4",
52
52
  "@socket.io/redis-adapter": "8.3.0",
53
53
  "@socket.io/redis-emitter": "5.1.0",
54
- "@tatara/observability": "0.1.0",
54
+ "@tatara/observability": "0.2.0",
55
55
  "@opentelemetry/api": "1.10.0",
56
56
  "@opentelemetry/sdk-node": "0.300.0",
57
57
  "@opentelemetry/sdk-trace": "3.0.0",
@@ -61,5 +61,6 @@
61
61
  "@opentelemetry/exporter-metrics-otlp-http": "0.300.0",
62
62
  "@opentelemetry/auto-instrumentations-node": "0.82.0",
63
63
  "@opentelemetry/instrumentation": "0.300.0",
64
- "@hono/otel": "1.3.0"
64
+ "@hono/otel": "1.3.0",
65
+ "import-in-the-middle": "3.5.3"
65
66
  }