@smartcrab/contracts-management 0.1.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.
Files changed (73) hide show
  1. package/LICENSE +202 -0
  2. package/dist/api-keys.d.ts +251 -0
  3. package/dist/api-keys.js +78 -0
  4. package/dist/audit-events.d.ts +94 -0
  5. package/dist/audit-events.js +45 -0
  6. package/dist/billing.d.ts +30 -0
  7. package/dist/billing.js +30 -0
  8. package/dist/clients.d.ts +328 -0
  9. package/dist/clients.js +141 -0
  10. package/dist/common.d.ts +95 -0
  11. package/dist/common.js +99 -0
  12. package/dist/connections.d.ts +201 -0
  13. package/dist/connections.js +107 -0
  14. package/dist/domains.d.ts +167 -0
  15. package/dist/domains.js +74 -0
  16. package/dist/environments.d.ts +194 -0
  17. package/dist/environments.js +101 -0
  18. package/dist/index.d.ts +27 -0
  19. package/dist/index.js +27 -0
  20. package/dist/primitives.d.ts +49 -0
  21. package/dist/primitives.js +56 -0
  22. package/dist/projects.d.ts +78 -0
  23. package/dist/projects.js +49 -0
  24. package/dist/rbac.d.ts +222 -0
  25. package/dist/rbac.js +226 -0
  26. package/dist/usage.d.ts +72 -0
  27. package/dist/usage.js +52 -0
  28. package/dist/user-jobs.d.ts +253 -0
  29. package/dist/user-jobs.js +95 -0
  30. package/dist/user-subresources.d.ts +161 -0
  31. package/dist/user-subresources.js +85 -0
  32. package/dist/users.d.ts +193 -0
  33. package/dist/users.js +112 -0
  34. package/dist/webhooks.d.ts +306 -0
  35. package/dist/webhooks.js +134 -0
  36. package/dist/workspaces.d.ts +172 -0
  37. package/dist/workspaces.js +77 -0
  38. package/package.json +42 -0
  39. package/src/api-keys.test.ts +86 -0
  40. package/src/api-keys.ts +97 -0
  41. package/src/audit-events.test.ts +44 -0
  42. package/src/audit-events.ts +57 -0
  43. package/src/billing.test.ts +57 -0
  44. package/src/billing.ts +46 -0
  45. package/src/clients.test.ts +169 -0
  46. package/src/clients.ts +182 -0
  47. package/src/common.test.ts +102 -0
  48. package/src/common.ts +127 -0
  49. package/src/connections.test.ts +65 -0
  50. package/src/connections.ts +134 -0
  51. package/src/domains.test.ts +48 -0
  52. package/src/domains.ts +104 -0
  53. package/src/environments.test.ts +67 -0
  54. package/src/environments.ts +133 -0
  55. package/src/index.ts +27 -0
  56. package/src/primitives.ts +100 -0
  57. package/src/projects.test.ts +49 -0
  58. package/src/projects.ts +72 -0
  59. package/src/rbac.test.ts +148 -0
  60. package/src/rbac.ts +250 -0
  61. package/src/secret-fields.test.ts +84 -0
  62. package/src/usage.test.ts +57 -0
  63. package/src/usage.ts +66 -0
  64. package/src/user-jobs.test.ts +86 -0
  65. package/src/user-jobs.ts +120 -0
  66. package/src/user-subresources.test.ts +115 -0
  67. package/src/user-subresources.ts +128 -0
  68. package/src/users.test.ts +91 -0
  69. package/src/users.ts +149 -0
  70. package/src/webhooks.test.ts +187 -0
  71. package/src/webhooks.ts +182 -0
  72. package/src/workspaces.test.ts +127 -0
  73. package/src/workspaces.ts +106 -0
@@ -0,0 +1,127 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import {
3
+ workspaceCreateRequestSchema,
4
+ workspaceListResponseSchema,
5
+ workspaceMemberListResponseSchema,
6
+ workspaceMemberSchema,
7
+ workspaceSchema,
8
+ workspaceUpdateRequestSchema,
9
+ } from "./workspaces.js";
10
+
11
+ const VALID_WORKSPACE = {
12
+ id: `wsp_${"a".repeat(32)}`,
13
+ name: "Acme Inc",
14
+ slug: "acme-inc",
15
+ status: "active",
16
+ stripe_customer_id: "cus_abc123",
17
+ created_at: "2026-08-02T08:10:00Z",
18
+ updated_at: "2026-08-02T08:10:00Z",
19
+ deleted_at: null,
20
+ };
21
+
22
+ describe("workspaceSchema (design.md §11.1)", () => {
23
+ test("accepts a valid workspace", () => {
24
+ expect(workspaceSchema.safeParse(VALID_WORKSPACE).success).toBe(true);
25
+ });
26
+
27
+ test("accepts a null stripe_customer_id (no billing set up yet)", () => {
28
+ expect(
29
+ workspaceSchema.safeParse({ ...VALID_WORKSPACE, stripe_customer_id: null }).success,
30
+ ).toBe(true);
31
+ });
32
+
33
+ test("rejects an unknown top-level key", () => {
34
+ expect(
35
+ workspaceSchema.safeParse({ ...VALID_WORKSPACE, control_database_key: "control_01" }).success,
36
+ ).toBe(false);
37
+ });
38
+
39
+ test("rejects an unknown status value", () => {
40
+ expect(workspaceSchema.safeParse({ ...VALID_WORKSPACE, status: "archived" }).success).toBe(
41
+ false,
42
+ );
43
+ });
44
+ });
45
+
46
+ describe("workspaceCreateRequestSchema", () => {
47
+ test("slug is optional", () => {
48
+ expect(workspaceCreateRequestSchema.safeParse({ name: "Acme Inc" }).success).toBe(true);
49
+ });
50
+
51
+ test("rejects an unknown field (e.g. attempting to set status at creation)", () => {
52
+ expect(
53
+ workspaceCreateRequestSchema.safeParse({ name: "Acme Inc", status: "active" }).success,
54
+ ).toBe(false);
55
+ });
56
+ });
57
+
58
+ describe("workspaceUpdateRequestSchema", () => {
59
+ test("does not allow patching status (Owner-only lifecycle transitions, design.md §27.2)", () => {
60
+ expect(workspaceUpdateRequestSchema.safeParse({ status: "suspended" }).success).toBe(false);
61
+ });
62
+
63
+ test("accepts an empty patch", () => {
64
+ expect(workspaceUpdateRequestSchema.safeParse({}).success).toBe(true);
65
+ });
66
+ });
67
+
68
+ describe("workspaceListResponseSchema", () => {
69
+ test("wraps a cursor page of workspaces", () => {
70
+ expect(
71
+ workspaceListResponseSchema.safeParse({ items: [VALID_WORKSPACE], next_cursor: null })
72
+ .success,
73
+ ).toBe(true);
74
+ });
75
+ });
76
+
77
+ const VALID_MEMBER = {
78
+ platform_user_id: "usr_owner",
79
+ role: "owner",
80
+ created_at: "2026-08-02T08:10:00Z",
81
+ updated_at: "2026-08-02T08:10:00Z",
82
+ };
83
+
84
+ describe("workspaceMemberSchema (design.md §11.2, §27.2)", () => {
85
+ test("accepts a valid member", () => {
86
+ expect(workspaceMemberSchema.safeParse(VALID_MEMBER).success).toBe(true);
87
+ });
88
+
89
+ test("accepts every design.md §27.2 role", () => {
90
+ for (const role of ["owner", "admin", "developer", "support", "billing", "viewer"]) {
91
+ expect(workspaceMemberSchema.safeParse({ ...VALID_MEMBER, role }).success).toBe(true);
92
+ }
93
+ });
94
+
95
+ test("rejects an unknown role", () => {
96
+ expect(workspaceMemberSchema.safeParse({ ...VALID_MEMBER, role: "superadmin" }).success).toBe(
97
+ false,
98
+ );
99
+ });
100
+
101
+ test("rejects an unknown top-level key (no credential/session material belongs here)", () => {
102
+ expect(workspaceMemberSchema.safeParse({ ...VALID_MEMBER, password: "hunter2" }).success).toBe(
103
+ false,
104
+ );
105
+ expect(workspaceMemberSchema.safeParse({ ...VALID_MEMBER, session_token: "abc" }).success).toBe(
106
+ false,
107
+ );
108
+ });
109
+ });
110
+
111
+ describe("workspaceMemberListResponseSchema", () => {
112
+ test("wraps a cursor page of members", () => {
113
+ expect(
114
+ workspaceMemberListResponseSchema.safeParse({ items: [VALID_MEMBER], next_cursor: null })
115
+ .success,
116
+ ).toBe(true);
117
+ });
118
+
119
+ test("rejects an item carrying a credential field", () => {
120
+ expect(
121
+ workspaceMemberListResponseSchema.safeParse({
122
+ items: [{ ...VALID_MEMBER, password_hash: "abc" }],
123
+ next_cursor: null,
124
+ }).success,
125
+ ).toBe(false);
126
+ });
127
+ });
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ import { cursorPageSchema, paginationQuerySchema } from "./common.js";
3
+ import { slugSchema, workspaceIdSchema } from "./primitives.js";
4
+ import { workspaceRoleSchema } from "./rbac.js";
5
+
6
+ /**
7
+ * `workspaces` (design.md §11.1, §7.1). No secret/internal-routing columns
8
+ * exist on this table to exclude; `stripe_customer_id` is an opaque Stripe
9
+ * identifier (not a secret — it carries no ability to act on the customer)
10
+ * and is useful to dashboard/billing UI, so it is exposed as nullable.
11
+ */
12
+ export const WORKSPACE_STATUSES = ["active", "past_due", "suspended", "deleted"] as const;
13
+ export const workspaceStatusSchema = z.enum(WORKSPACE_STATUSES);
14
+ export type WorkspaceStatus = (typeof WORKSPACE_STATUSES)[number];
15
+
16
+ export const workspaceSchema = z.strictObject({
17
+ id: workspaceIdSchema,
18
+ name: z.string().min(1).max(200),
19
+ slug: slugSchema,
20
+ status: workspaceStatusSchema,
21
+ stripe_customer_id: z.string().min(1).nullable(),
22
+ created_at: z.iso.datetime(),
23
+ updated_at: z.iso.datetime(),
24
+ deleted_at: z.iso.datetime().nullable(),
25
+ });
26
+ export type Workspace = z.infer<typeof workspaceSchema>;
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // GET /workspaces
30
+ // ---------------------------------------------------------------------------
31
+
32
+ export const workspaceListQuerySchema = paginationQuerySchema.extend({
33
+ status: workspaceStatusSchema.optional(),
34
+ });
35
+ export type WorkspaceListQuery = z.infer<typeof workspaceListQuerySchema>;
36
+
37
+ export const workspaceListResponseSchema = cursorPageSchema(workspaceSchema);
38
+ export type WorkspaceListResponse = z.infer<typeof workspaceListResponseSchema>;
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // POST /workspaces
42
+ // ---------------------------------------------------------------------------
43
+
44
+ /** `slug` is optional: when omitted, the server derives one from `name` (design.md §11.1 `slug UNIQUE`). */
45
+ export const workspaceCreateRequestSchema = z.strictObject({
46
+ name: z.string().min(1).max(200),
47
+ slug: slugSchema.optional(),
48
+ });
49
+ export type WorkspaceCreateRequest = z.infer<typeof workspaceCreateRequestSchema>;
50
+
51
+ export const workspaceCreateResponseSchema = workspaceSchema;
52
+ export type WorkspaceCreateResponse = z.infer<typeof workspaceCreateResponseSchema>;
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // GET /workspaces/:workspaceId
56
+ // ---------------------------------------------------------------------------
57
+
58
+ export const workspaceGetResponseSchema = workspaceSchema;
59
+ export type WorkspaceGetResponse = z.infer<typeof workspaceGetResponseSchema>;
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // PATCH /workspaces/:workspaceId
63
+ // ---------------------------------------------------------------------------
64
+
65
+ /**
66
+ * `status` is deliberately excluded: workspace suspension/deletion are
67
+ * billing- and Owner-driven lifecycle transitions (design.md §27.2 "Owner: 全
68
+ * 操作、課金、Workspace削除"), not a generic field patch.
69
+ */
70
+ export const workspaceUpdateRequestSchema = z.strictObject({
71
+ name: z.string().min(1).max(200).optional(),
72
+ slug: slugSchema.optional(),
73
+ });
74
+ export type WorkspaceUpdateRequest = z.infer<typeof workspaceUpdateRequestSchema>;
75
+
76
+ export const workspaceUpdateResponseSchema = workspaceSchema;
77
+ export type WorkspaceUpdateResponse = z.infer<typeof workspaceUpdateResponseSchema>;
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // GET /workspaces/:workspaceId/members
81
+ // ---------------------------------------------------------------------------
82
+
83
+ /**
84
+ * `workspace_members` (design.md §11.2, §27.2). Exposes only the fields the
85
+ * assignment enumerates — `platform_user_id`/`role`/`created_at`/
86
+ * `updated_at` — no credential or session material (this table carries none
87
+ * anyway; those four columns are its entire shape). `platform_user_id` is a
88
+ * plain non-empty string rather than `userIdSchema`: it identifies a
89
+ * platform (dashboard) user of this Control Plane's own dogfooded
90
+ * Environment (design.md §27.1), a distinct identifier space from the
91
+ * `usr_...`-pattern end users `userIdSchema` models elsewhere in this
92
+ * package.
93
+ */
94
+ export const workspaceMemberSchema = z.strictObject({
95
+ platform_user_id: z.string().min(1),
96
+ role: workspaceRoleSchema,
97
+ created_at: z.iso.datetime(),
98
+ updated_at: z.iso.datetime(),
99
+ });
100
+ export type WorkspaceMember = z.infer<typeof workspaceMemberSchema>;
101
+
102
+ export const workspaceMemberListQuerySchema = paginationQuerySchema;
103
+ export type WorkspaceMemberListQuery = z.infer<typeof workspaceMemberListQuerySchema>;
104
+
105
+ export const workspaceMemberListResponseSchema = cursorPageSchema(workspaceMemberSchema);
106
+ export type WorkspaceMemberListResponse = z.infer<typeof workspaceMemberListResponseSchema>;