@getstrata/core 0.5.42 → 0.5.43

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.
@@ -0,0 +1,178 @@
1
+ // @bun
2
+ // ../../src/config/app.ts
3
+ var appConfig = {
4
+ name: "WorkHub",
5
+ env: process.env.APP_ENV ?? "local",
6
+ debug: (process.env.APP_DEBUG ?? "true") !== "false",
7
+ url: process.env.APP_URL ?? "http://localhost:3000",
8
+ apiPrefix: process.env.API_PREFIX ?? "/api/v1"
9
+ };
10
+
11
+ // ../../src/core/openapi/generator.ts
12
+ var PUBLIC_ROUTE_DESCRIPTIONS = {
13
+ "GET /auth/me": "Current authenticated user",
14
+ "POST /auth/login": "Login with email and password",
15
+ "GET /auth/tokens": "List API tokens",
16
+ "POST /auth/tokens": "Create API token",
17
+ "DELETE /auth/tokens/:id": "Revoke API token",
18
+ "GET /users/me/export": "GDPR export of user data",
19
+ "DELETE /users/me": "GDPR account erasure (anonymize user, revoke tokens)",
20
+ "GET /organizations": "List organizations",
21
+ "POST /organizations": "Create organization",
22
+ "GET /organizations/:id/members": "List organization members",
23
+ "GET /projects": "List projects",
24
+ "POST /projects": "Create project",
25
+ "GET /tasks": "List tasks",
26
+ "POST /tasks": "Create task",
27
+ "GET /search": "Full-text search tasks and comments",
28
+ "GET /audit-logs": "List audit log entries",
29
+ "GET /webhooks": "List webhooks",
30
+ "POST /webhooks": "Create webhook",
31
+ "GET /reports/summary": "Cross-module summary report",
32
+ "GET /admin/stats": "Platform statistics",
33
+ "GET /admin/tenants": "List tenants",
34
+ "GET /admin/features": "Runtime feature flags",
35
+ "GET /billing/subscription": "Current tenant subscription",
36
+ "POST /billing/webhooks/stripe": "Stripe webhook receiver (stub)",
37
+ "GET /scim/v2/Users": "SCIM list users",
38
+ "POST /scim/v2/Users": "SCIM create user",
39
+ "GET /scim/v2/Groups": "SCIM list groups (organizations)",
40
+ "GET /health": "Liveness probe",
41
+ "GET /ready": "Readiness probe",
42
+ "GET /metrics": "Prometheus metrics"
43
+ };
44
+ function toOpenApiPath(path) {
45
+ return path.replace(/:([A-Za-z_]+)/g, "{$1}");
46
+ }
47
+ function requiresBearerAuth(path, method) {
48
+ if (path.startsWith("/auth/login") || path.startsWith("/auth/oauth")) {
49
+ return false;
50
+ }
51
+ if (path.startsWith("/scim/") || path.startsWith("/billing/webhooks/")) {
52
+ return false;
53
+ }
54
+ if (["/health", "/ready", "/metrics", "/"].includes(path)) {
55
+ return false;
56
+ }
57
+ if (method === "GET" && ["/organizations", "/projects", "/tasks", "/search"].some((prefix) => path.startsWith(prefix))) {
58
+ return false;
59
+ }
60
+ return path.startsWith("/auth/") || ["POST", "PATCH", "PUT", "DELETE"].includes(method);
61
+ }
62
+ function generateOpenApiSpec(routes) {
63
+ const paths = {};
64
+ for (const route of routes) {
65
+ const openApiPath = toOpenApiPath(route.path);
66
+ const method = route.method.toLowerCase();
67
+ const description = PUBLIC_ROUTE_DESCRIPTIONS[`${route.method} ${route.path}`] ?? `${route.method} ${route.path}`;
68
+ paths[openApiPath] ??= {};
69
+ paths[openApiPath][method] = {
70
+ summary: description,
71
+ ...requiresBearerAuth(route.path, route.method) ? { security: [{ bearerAuth: [] }] } : {},
72
+ responses: {
73
+ "200": { description: "OK" },
74
+ "201": { description: "Created" },
75
+ "204": { description: "No Content" },
76
+ "400": { description: "Bad Request" },
77
+ "401": { description: "Unauthorized" },
78
+ "403": { description: "Forbidden" },
79
+ "404": { description: "Not Found" },
80
+ "422": { description: "Validation Error" }
81
+ }
82
+ };
83
+ }
84
+ return {
85
+ openapi: "3.1.0",
86
+ info: {
87
+ title: "WorkHub API",
88
+ version: "1.0.0"
89
+ },
90
+ servers: [
91
+ { url: `${appConfig.url}${appConfig.apiPrefix}`, description: "WorkHub API" },
92
+ { url: appConfig.url, description: "Root (health, metrics, SCIM)" }
93
+ ],
94
+ paths,
95
+ components: {
96
+ securitySchemes: {
97
+ bearerAuth: {
98
+ type: "http",
99
+ scheme: "bearer"
100
+ }
101
+ },
102
+ schemas: {
103
+ ErrorResponse: {
104
+ type: "object",
105
+ properties: {
106
+ error: { type: "string" },
107
+ details: { type: "object", additionalProperties: true }
108
+ },
109
+ required: ["error"]
110
+ },
111
+ UserResource: {
112
+ type: "object",
113
+ properties: {
114
+ id: { type: "integer" },
115
+ name: { type: "string" },
116
+ email: { type: "string" },
117
+ role: { type: "string" }
118
+ }
119
+ },
120
+ OrganizationResource: {
121
+ type: "object",
122
+ properties: {
123
+ id: { type: "integer" },
124
+ name: { type: "string" },
125
+ slug: { type: "string" }
126
+ }
127
+ },
128
+ PaginatedMeta: {
129
+ type: "object",
130
+ properties: {
131
+ page: { type: "integer" },
132
+ per_page: { type: "integer" },
133
+ total: { type: "integer" },
134
+ last_page: { type: "integer" }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ };
140
+ }
141
+ function renderOpenApiDocument(spec) {
142
+ return `${JSON.stringify(spec, null, 2)}
143
+ `;
144
+ }
145
+ function toMethodName(method, path, apiPrefix) {
146
+ const relativePath = path.startsWith(apiPrefix) ? path.slice(apiPrefix.length) || "/" : path;
147
+ const segments = relativePath.replace(/\{|\}/g, "").split("/").filter(Boolean).flatMap((segment) => segment.split("-")).map((segment) => segment.replace(/[^a-zA-Z0-9]/g, "")).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1));
148
+ return `${method.toLowerCase()}${segments.join("")}`;
149
+ }
150
+ function toRequestPath(path, apiPrefix) {
151
+ return path.startsWith(apiPrefix) ? path.slice(apiPrefix.length) || "/" : path;
152
+ }
153
+ function renderTypeScriptSdk(spec, apiPrefix = "/api/v1") {
154
+ const lines = [
155
+ "export class WorkHubClient {",
156
+ ` constructor(private readonly baseUrl = "${spec.servers[0]?.url ?? ""}") {}`,
157
+ "",
158
+ " private async request(path: string, init: RequestInit = {}): Promise<Response> {",
159
+ ` return await fetch(\`\${this.baseUrl}\${path}\`, init);`,
160
+ " }",
161
+ ""
162
+ ];
163
+ for (const [path, methods] of Object.entries(spec.paths)) {
164
+ const requestPath = toRequestPath(path, apiPrefix);
165
+ for (const method of Object.keys(methods)) {
166
+ const functionName = toMethodName(method, path, apiPrefix);
167
+ lines.push(` async ${functionName}(init: RequestInit = {}): Promise<Response> {`, ` return await this.request("${requestPath}", { ...init, method: "${method.toUpperCase()}" });`, " }", "");
168
+ }
169
+ }
170
+ lines.push("}", "");
171
+ return lines.join(`
172
+ `);
173
+ }
174
+ export {
175
+ renderTypeScriptSdk,
176
+ renderOpenApiDocument,
177
+ generateOpenApiSpec
178
+ };
@@ -0,0 +1,28 @@
1
+ // @bun
2
+ // ../../src/core/openapi/validate.ts
3
+ function validateOpenApiSpec(spec) {
4
+ const errors = [];
5
+ if (!spec.openapi.startsWith("3.")) {
6
+ errors.push("OpenAPI version must be 3.x.");
7
+ }
8
+ if (Object.keys(spec.paths).length === 0) {
9
+ errors.push("OpenAPI spec must include at least one path.");
10
+ }
11
+ for (const [path, methods] of Object.entries(spec.paths)) {
12
+ if (!path.startsWith("/")) {
13
+ errors.push(`Path must start with '/': ${path}`);
14
+ }
15
+ for (const [method, operation] of Object.entries(methods)) {
16
+ if (!("responses" in operation)) {
17
+ errors.push(`Operation ${method.toUpperCase()} ${path} is missing responses.`);
18
+ }
19
+ }
20
+ }
21
+ if (!spec.components.securitySchemes.bearerAuth) {
22
+ errors.push("Missing bearerAuth security scheme.");
23
+ }
24
+ return errors;
25
+ }
26
+ export {
27
+ validateOpenApiSpec
28
+ };
@@ -1925,6 +1925,45 @@ class Blueprint {
1925
1925
  });
1926
1926
  }
1927
1927
  }
1928
+ // ../../src/core/database/schema/driver.ts
1929
+ function normalizeConnectionName(connection) {
1930
+ const normalized = connection.trim().toLowerCase();
1931
+ if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
1932
+ return "pgsql";
1933
+ }
1934
+ if (normalized === "mysql" || normalized === "mariadb") {
1935
+ return "mysql";
1936
+ }
1937
+ if (normalized === "sqlite") {
1938
+ return "sqlite";
1939
+ }
1940
+ throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
1941
+ }
1942
+ function resolveDriverFromUrl(url) {
1943
+ const normalized = url.trim().toLowerCase();
1944
+ if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
1945
+ return "pgsql";
1946
+ }
1947
+ if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
1948
+ return "mysql";
1949
+ }
1950
+ if (normalized.startsWith("sqlite:")) {
1951
+ return "sqlite";
1952
+ }
1953
+ return null;
1954
+ }
1955
+ function resolveDatabaseDriver(options = {}) {
1956
+ const connection = options.connection ?? process.env.DB_CONNECTION;
1957
+ if (connection) {
1958
+ return normalizeConnectionName(connection);
1959
+ }
1960
+ const url = options.url ?? process.env.DATABASE_URL ?? "";
1961
+ const fromUrl = resolveDriverFromUrl(url);
1962
+ if (fromUrl) {
1963
+ return fromUrl;
1964
+ }
1965
+ return "pgsql";
1966
+ }
1928
1967
  // ../../src/core/database/schema/errors.ts
1929
1968
  class UnsupportedSchemaFeatureError extends Error {
1930
1969
  constructor(feature, driver) {
@@ -2269,6 +2308,25 @@ class SchemaBuilder {
2269
2308
  }
2270
2309
  }
2271
2310
  }
2311
+
2312
+ class Schema {
2313
+ static builder(driver) {
2314
+ return new SchemaBuilder(driver ?? resolveDatabaseDriver());
2315
+ }
2316
+ static async run(db, driver, callback) {
2317
+ const schema = Schema.builder(driver);
2318
+ await callback(schema);
2319
+ await schema.execute(db);
2320
+ }
2321
+ }
2322
+ function createSchemaBuilder(db, driver) {
2323
+ const builder = Schema.builder(driver);
2324
+ return Object.assign(builder, {
2325
+ async commit() {
2326
+ await builder.execute(db);
2327
+ }
2328
+ });
2329
+ }
2272
2330
  // ../../src/core/database/table.ts
2273
2331
  function defineTable(definition) {
2274
2332
  return definition;
@@ -1925,6 +1925,45 @@ class Blueprint {
1925
1925
  });
1926
1926
  }
1927
1927
  }
1928
+ // ../../src/core/database/schema/driver.ts
1929
+ function normalizeConnectionName(connection) {
1930
+ const normalized = connection.trim().toLowerCase();
1931
+ if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
1932
+ return "pgsql";
1933
+ }
1934
+ if (normalized === "mysql" || normalized === "mariadb") {
1935
+ return "mysql";
1936
+ }
1937
+ if (normalized === "sqlite") {
1938
+ return "sqlite";
1939
+ }
1940
+ throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
1941
+ }
1942
+ function resolveDriverFromUrl(url) {
1943
+ const normalized = url.trim().toLowerCase();
1944
+ if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
1945
+ return "pgsql";
1946
+ }
1947
+ if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
1948
+ return "mysql";
1949
+ }
1950
+ if (normalized.startsWith("sqlite:")) {
1951
+ return "sqlite";
1952
+ }
1953
+ return null;
1954
+ }
1955
+ function resolveDatabaseDriver(options = {}) {
1956
+ const connection = options.connection ?? process.env.DB_CONNECTION;
1957
+ if (connection) {
1958
+ return normalizeConnectionName(connection);
1959
+ }
1960
+ const url = options.url ?? process.env.DATABASE_URL ?? "";
1961
+ const fromUrl = resolveDriverFromUrl(url);
1962
+ if (fromUrl) {
1963
+ return fromUrl;
1964
+ }
1965
+ return "pgsql";
1966
+ }
1928
1967
  // ../../src/core/database/schema/errors.ts
1929
1968
  class UnsupportedSchemaFeatureError extends Error {
1930
1969
  constructor(feature, driver) {
@@ -2269,6 +2308,25 @@ class SchemaBuilder {
2269
2308
  }
2270
2309
  }
2271
2310
  }
2311
+
2312
+ class Schema {
2313
+ static builder(driver) {
2314
+ return new SchemaBuilder(driver ?? resolveDatabaseDriver());
2315
+ }
2316
+ static async run(db, driver, callback) {
2317
+ const schema = Schema.builder(driver);
2318
+ await callback(schema);
2319
+ await schema.execute(db);
2320
+ }
2321
+ }
2322
+ function createSchemaBuilder(db, driver) {
2323
+ const builder = Schema.builder(driver);
2324
+ return Object.assign(builder, {
2325
+ async commit() {
2326
+ await builder.execute(db);
2327
+ }
2328
+ });
2329
+ }
2272
2330
  // ../../src/core/database/table.ts
2273
2331
  function defineTable(definition) {
2274
2332
  return definition;
@@ -1925,6 +1925,45 @@ class Blueprint {
1925
1925
  });
1926
1926
  }
1927
1927
  }
1928
+ // ../../src/core/database/schema/driver.ts
1929
+ function normalizeConnectionName(connection) {
1930
+ const normalized = connection.trim().toLowerCase();
1931
+ if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
1932
+ return "pgsql";
1933
+ }
1934
+ if (normalized === "mysql" || normalized === "mariadb") {
1935
+ return "mysql";
1936
+ }
1937
+ if (normalized === "sqlite") {
1938
+ return "sqlite";
1939
+ }
1940
+ throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
1941
+ }
1942
+ function resolveDriverFromUrl(url) {
1943
+ const normalized = url.trim().toLowerCase();
1944
+ if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
1945
+ return "pgsql";
1946
+ }
1947
+ if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
1948
+ return "mysql";
1949
+ }
1950
+ if (normalized.startsWith("sqlite:")) {
1951
+ return "sqlite";
1952
+ }
1953
+ return null;
1954
+ }
1955
+ function resolveDatabaseDriver(options = {}) {
1956
+ const connection = options.connection ?? process.env.DB_CONNECTION;
1957
+ if (connection) {
1958
+ return normalizeConnectionName(connection);
1959
+ }
1960
+ const url = options.url ?? process.env.DATABASE_URL ?? "";
1961
+ const fromUrl = resolveDriverFromUrl(url);
1962
+ if (fromUrl) {
1963
+ return fromUrl;
1964
+ }
1965
+ return "pgsql";
1966
+ }
1928
1967
  // ../../src/core/database/schema/errors.ts
1929
1968
  class UnsupportedSchemaFeatureError extends Error {
1930
1969
  constructor(feature, driver) {
@@ -2269,6 +2308,25 @@ class SchemaBuilder {
2269
2308
  }
2270
2309
  }
2271
2310
  }
2311
+
2312
+ class Schema {
2313
+ static builder(driver) {
2314
+ return new SchemaBuilder(driver ?? resolveDatabaseDriver());
2315
+ }
2316
+ static async run(db, driver, callback) {
2317
+ const schema = Schema.builder(driver);
2318
+ await callback(schema);
2319
+ await schema.execute(db);
2320
+ }
2321
+ }
2322
+ function createSchemaBuilder(db, driver) {
2323
+ const builder = Schema.builder(driver);
2324
+ return Object.assign(builder, {
2325
+ async commit() {
2326
+ await builder.execute(db);
2327
+ }
2328
+ });
2329
+ }
2272
2330
  // ../../src/core/database/table.ts
2273
2331
  function defineTable(definition) {
2274
2332
  return definition;
@@ -1946,6 +1946,45 @@ class Blueprint {
1946
1946
  });
1947
1947
  }
1948
1948
  }
1949
+ // ../../src/core/database/schema/driver.ts
1950
+ function normalizeConnectionName(connection) {
1951
+ const normalized = connection.trim().toLowerCase();
1952
+ if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
1953
+ return "pgsql";
1954
+ }
1955
+ if (normalized === "mysql" || normalized === "mariadb") {
1956
+ return "mysql";
1957
+ }
1958
+ if (normalized === "sqlite") {
1959
+ return "sqlite";
1960
+ }
1961
+ throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
1962
+ }
1963
+ function resolveDriverFromUrl(url) {
1964
+ const normalized = url.trim().toLowerCase();
1965
+ if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
1966
+ return "pgsql";
1967
+ }
1968
+ if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
1969
+ return "mysql";
1970
+ }
1971
+ if (normalized.startsWith("sqlite:")) {
1972
+ return "sqlite";
1973
+ }
1974
+ return null;
1975
+ }
1976
+ function resolveDatabaseDriver(options = {}) {
1977
+ const connection = options.connection ?? process.env.DB_CONNECTION;
1978
+ if (connection) {
1979
+ return normalizeConnectionName(connection);
1980
+ }
1981
+ const url = options.url ?? process.env.DATABASE_URL ?? "";
1982
+ const fromUrl = resolveDriverFromUrl(url);
1983
+ if (fromUrl) {
1984
+ return fromUrl;
1985
+ }
1986
+ return "pgsql";
1987
+ }
1949
1988
  // ../../src/core/database/schema/errors.ts
1950
1989
  class UnsupportedSchemaFeatureError extends Error {
1951
1990
  constructor(feature, driver) {
@@ -2290,6 +2329,25 @@ class SchemaBuilder {
2290
2329
  }
2291
2330
  }
2292
2331
  }
2332
+
2333
+ class Schema {
2334
+ static builder(driver) {
2335
+ return new SchemaBuilder(driver ?? resolveDatabaseDriver());
2336
+ }
2337
+ static async run(db, driver, callback) {
2338
+ const schema = Schema.builder(driver);
2339
+ await callback(schema);
2340
+ await schema.execute(db);
2341
+ }
2342
+ }
2343
+ function createSchemaBuilder(db, driver) {
2344
+ const builder = Schema.builder(driver);
2345
+ return Object.assign(builder, {
2346
+ async commit() {
2347
+ await builder.execute(db);
2348
+ }
2349
+ });
2350
+ }
2293
2351
  // ../../src/core/database/table.ts
2294
2352
  function defineTable(definition) {
2295
2353
  return definition;
@@ -0,0 +1,17 @@
1
+ // @bun
2
+ // ../../src/core/runtime/asyncContextStore.ts
3
+ import { AsyncLocalStorage } from "async_hooks";
4
+ function createAsyncContextStore(key) {
5
+ const symbol = Symbol.for(key);
6
+ const globalRecord = globalThis;
7
+ const existing = globalRecord[symbol];
8
+ if (existing) {
9
+ return existing;
10
+ }
11
+ const store = new AsyncLocalStorage;
12
+ globalRecord[symbol] = store;
13
+ return store;
14
+ }
15
+ export {
16
+ createAsyncContextStore
17
+ };