@tatara/cli 0.11.0 → 0.12.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.0",
3
+ "version": "0.12.0",
4
4
  "description": "Create and grow opinionated Hono backends",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -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
 
@@ -1,4 +1,4 @@
1
- import { createMailer, renderEmail } from "@tatara/email";
1
+ import { createMailer, type EmailDefinition, renderEmail } from "@tatara/email";
2
2
  import { env } from "#core/config/env";
3
3
  import { logger } from "#core/logging/logger";
4
4
  import type { emails } from "#generated/emails";
@@ -27,7 +27,8 @@ export async function sendTemplate<N extends keyof Templates & string>(
27
27
  ): Promise<void> {
28
28
  // loaded on first use: core doesn't import module code until it's needed
29
29
  const { emails: all } = await import("#generated/emails");
30
- const template = all.find((e) => e.name === name);
30
+ // read as any template: an app without templates has an empty list
31
+ const template = (all as readonly EmailDefinition[]).find((e) => e.name === name);
31
32
  if (!template) throw new Error(`No email template named "${name}"`);
32
33
  await sendEmail({ to, ...renderEmail(template, data, language) });
33
34
  }
@@ -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
38
  "@tatara/auth": "0.2.0",
39
39
  "@tatara/keycloak": "0.1.2",
40
- "@tatara/authz": "0.3.1",
40
+ "@tatara/authz": "0.4.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
  }