@rebasepro/server 0.10.0 → 0.10.1-canary.0a881d4

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 (35) hide show
  1. package/bin/rebase-server.js +55 -0
  2. package/dist/api/ast-schema-editor.d.ts +10 -0
  3. package/dist/api/contract-routes.d.ts +36 -0
  4. package/dist/api/rest/api-generator.d.ts +9 -1
  5. package/dist/api/rest/query-parser.d.ts +17 -1
  6. package/dist/auth/interfaces.d.ts +65 -2
  7. package/dist/auth/jwt.d.ts +17 -0
  8. package/dist/auth/routes.d.ts +16 -0
  9. package/dist/boot/boot.d.ts +59 -0
  10. package/dist/boot/bundle.d.ts +134 -0
  11. package/dist/boot/driver.d.ts +57 -0
  12. package/dist/boot/env.d.ts +129 -0
  13. package/dist/boot/options.d.ts +20 -0
  14. package/dist/boot/sources.d.ts +77 -0
  15. package/dist/index.d.ts +15 -0
  16. package/dist/index.es.js +6186 -2213
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/init/middlewares.d.ts +9 -0
  19. package/dist/init/storage.d.ts +27 -0
  20. package/dist/init.d.ts +43 -0
  21. package/dist/{jwt-B3zjddCa.js → jwt-Dj7r7QX7.js} +36 -24
  22. package/dist/{jwt-B3zjddCa.js.map → jwt-Dj7r7QX7.js.map} +1 -1
  23. package/dist/metrics/index.d.ts +83 -0
  24. package/dist/{openapi-generator-DqSIwNLV.js → openapi-generator-C3_pTcsb.js} +2 -3
  25. package/dist/{openapi-generator-DqSIwNLV.js.map → openapi-generator-C3_pTcsb.js.map} +1 -1
  26. package/dist/{schema-editor-routes-D3ef8zu1.js → schema-editor-routes-CF-g0gVN.js} +30 -2
  27. package/dist/schema-editor-routes-CF-g0gVN.js.map +1 -0
  28. package/dist/{src-CsHhSKbi.js → src-DRUswG_w.js} +276 -2
  29. package/dist/src-DRUswG_w.js.map +1 -0
  30. package/dist/storage/routes.d.ts +9 -1
  31. package/dist/storage/types.d.ts +1 -32
  32. package/dist/utils/dev-port.d.ts +5 -2
  33. package/package.json +12 -11
  34. package/dist/schema-editor-routes-D3ef8zu1.js.map +0 -1
  35. package/dist/src-CsHhSKbi.js.map +0 -1
@@ -0,0 +1,83 @@
1
+ import { Hono } from "hono";
2
+ import type { MiddlewareHandler } from "hono";
3
+ import type { HonoEnv } from "../api/types";
4
+ /**
5
+ * Runtime metrics, in Prometheus text format.
6
+ *
7
+ * The point of emitting these from the runtime rather than scraping the
8
+ * container is that only the runtime knows what a request *was*. A pod-level CPU
9
+ * graph cannot tell you that auth is slow while data is fine, or that one app is
10
+ * generating all the traffic. Surface by surface is the difference between a
11
+ * chart that looks like observability and one you can act on.
12
+ *
13
+ * Self-hosters get the same endpoint — this is a plain Prometheus target, not a
14
+ * hook into a hosted platform.
15
+ */
16
+ /** Which part of the API served a request. */
17
+ export type MetricSurface = "data" | "auth" | "storage" | "functions" | "admin" | "meta" | "other";
18
+ export declare class MetricsRegistry {
19
+ private requests;
20
+ private latency;
21
+ private gauges;
22
+ private counters;
23
+ readonly startedAt: number;
24
+ private static labelKey;
25
+ private static parseLabels;
26
+ /**
27
+ * Key a named series.
28
+ *
29
+ * Concatenating a name and its labels without a separator would let metric
30
+ * `rebase_x` with label `y=1` collide with a metric literally named
31
+ * `rebase_xy=1`.
32
+ */
33
+ private static namedKey;
34
+ private static splitNamedKey;
35
+ recordRequest(labels: Record<string, string>, durationMs: number): void;
36
+ incrementCounter(name: string, labels?: Record<string, string>, by?: number): void;
37
+ setGauge(name: string, value: number, labels?: Record<string, string>): void;
38
+ /** Prometheus escaping: backslash, quote and newline, in that order. */
39
+ private static formatLabels;
40
+ /** Group `name -> [[labelKey, value]]` for one-HELP-per-metric rendering. */
41
+ private static group;
42
+ render(): string;
43
+ }
44
+ /**
45
+ * Classify a path into the surface that served it.
46
+ *
47
+ * Path *shape*, never the full path: a label per entity id would create an
48
+ * unbounded set of time series, which is the classic way to take down a
49
+ * Prometheus. Collection slugs are bounded by the schema, so those are safe and
50
+ * genuinely useful.
51
+ */
52
+ export declare function classifySurface(pathname: string, basePath?: string): {
53
+ surface: MetricSurface;
54
+ collection?: string;
55
+ };
56
+ export interface MetricsHandle {
57
+ registry: MetricsRegistry;
58
+ middleware: MiddlewareHandler<HonoEnv>;
59
+ /**
60
+ * Restrict the `collection` label to names that actually exist.
61
+ *
62
+ * Called once the collections are known — the middleware has to be installed
63
+ * before them, since it must wrap every request. Until it is called, and for
64
+ * any name not in the set, the label is dropped: a path segment is attacker-
65
+ * controlled, and one series per value invented is unbounded memory.
66
+ */
67
+ setKnownCollections(slugs: Iterable<string>): void;
68
+ }
69
+ /**
70
+ * Build the request-timing middleware and the registry it feeds.
71
+ *
72
+ * Timing wraps `next()` in a `finally`, so a request that throws is still
73
+ * counted — an endpoint that only ever fails would otherwise be invisible in
74
+ * exactly the situation the metrics exist for.
75
+ */
76
+ export declare function createMetricsMiddleware(basePath?: string): MetricsHandle;
77
+ /**
78
+ * Mount the scrape endpoint.
79
+ *
80
+ * When a token is configured it is required, and compared in constant time — a
81
+ * timing oracle on a metrics token is a small thing, but it is free to avoid.
82
+ */
83
+ export declare function createMetricsRoutes(registry: MetricsRegistry, token?: string): Hono<HonoEnv>;
@@ -410,9 +410,8 @@ function convertPropertyToSchema(property) {
410
410
  if (sp.validation.matches !== void 0) base.pattern = String(sp.validation.matches);
411
411
  }
412
412
  if (sp.email) base.format = "email";
413
- if (sp.ui?.url) base.format = "uri";
413
+ if (sp.url) base.format = "uri";
414
414
  if (sp.storage) base.format = "uri";
415
- if (sp.ui?.markdown) base.description = (base.description || "") + " (Markdown)";
416
415
  return base;
417
416
  }
418
417
  case "number": {
@@ -589,4 +588,4 @@ function toPascalCase(str) {
589
588
  //#endregion
590
589
  export { generateOpenApiSpec };
591
590
 
592
- //# sourceMappingURL=openapi-generator-DqSIwNLV.js.map
591
+ //# sourceMappingURL=openapi-generator-C3_pTcsb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openapi-generator-DqSIwNLV.js","names":[],"sources":["../src/api/openapi-generator.ts"],"sourcesContent":["import { CollectionConfig, Property, StringProperty, NumberProperty, ArrayProperty, MapProperty, Relation, VectorProperty } from \"@rebasepro/types\";\n\n/**\n * OpenAPI 3.0.3 specification generator.\n *\n * Produces a spec that exactly mirrors the REST API consumed by the\n * Rebase SDK client (`@rebasepro/client`).\n *\n * Routes are mounted at `{basePath}/data/{slug}` by `initializeRebaseBackend`.\n */\n\nexport interface OpenApiGeneratorOptions {\n /** Base path for the API (e.g. \"/api\"). Defaults to \"/api\". */\n basePath?: string;\n /** Whether auth is enabled on data routes. Defaults to true. */\n requireAuth?: boolean;\n}\n\nexport function generateOpenApiSpec(\n collections: CollectionConfig[],\n options: OpenApiGeneratorOptions = {}\n): Record<string, unknown> {\n const basePath = options.basePath ?? \"/api\";\n const requireAuth = options.requireAuth ?? true;\n\n const spec: Record<string, unknown> = {\n openapi: \"3.0.3\",\n info: {\n title: \"Rebase API\",\n version: \"1.0.0\",\n description:\n \"Auto-generated REST API from Rebase collection definitions. \" +\n \"This is the same API consumed by the `@rebasepro/client` SDK.\"\n },\n servers: [\n {\n url: basePath,\n description: \"API Server\"\n }\n ],\n paths: {} as Record<string, unknown>,\n components: {\n schemas: {\n ErrorResponse: {\n type: \"object\",\n properties: {\n error: {\n type: \"object\",\n required: [\"message\", \"code\"],\n properties: {\n message: { type: \"string\" },\n code: { type: \"string\" },\n details: {}\n }\n }\n }\n },\n PaginationMeta: {\n type: \"object\",\n properties: {\n total: { type: \"integer\",\ndescription: \"Total number of matching records\" },\n limit: { type: \"integer\",\ndescription: \"Page size used for this query\" },\n offset: { type: \"integer\",\ndescription: \"Number of records skipped\" },\n hasMore: { type: \"boolean\",\ndescription: \"Whether more records exist beyond this page\" }\n }\n }\n } as Record<string, unknown>,\n securitySchemes: {} as Record<string, unknown>\n },\n tags: [] as Array<{ name: string; description?: string }>\n };\n\n // ── Security Schemes ─────────────────────────────────────────────────\n if (requireAuth) {\n (spec.components as Record<string, unknown>).securitySchemes = {\n bearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n description:\n \"JWT access token obtained from `POST /auth/login` or `POST /auth/register`. \" +\n \"Can also be a static service key for server-to-server authentication.\"\n },\n queryToken: {\n type: \"apiKey\",\n in: \"query\",\n name: \"token\",\n description: \"Alternative: pass the JWT or service key as a `token` query parameter.\"\n }\n };\n (spec as Record<string, unknown>).security = [\n { bearerAuth: [] },\n { queryToken: [] }\n ];\n }\n\n const paths = spec.paths as Record<string, unknown>;\n const schemas = (spec.components as Record<string, unknown>).schemas as Record<string, unknown>;\n const tags = spec.tags as Array<{ name: string; description?: string }>;\n\n // ── Collection routes ────────────────────────────────────────────────\n for (const collection of (collections || [])) {\n const schemaName = toPascalCase(collection.singularName || collection.name);\n const slug = collection.slug;\n\n tags.push({\n name: collection.name,\n description: collection.description || `CRUD operations for ${collection.name}`\n });\n\n // Build component schema for this collection\n schemas[schemaName] = buildCollectionSchema(collection);\n\n // Build an \"input\" schema (no read-only/auto fields like autoValue dates)\n schemas[`${schemaName}Input`] = buildCollectionInputSchema(collection);\n\n const dataPath = `/data/${slug}`;\n\n // ── GET /data/{slug} — List entities ──────────────────────────\n paths[dataPath] = {\n get: {\n tags: [collection.name],\n summary: `List ${collection.name}`,\n operationId: `list${schemaName}`,\n parameters: [\n { name: \"limit\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 20,\nmaximum: 100 },\ndescription: \"Maximum number of records to return\" },\n { name: \"offset\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 0 },\ndescription: \"Number of records to skip\" },\n { name: \"page\",\nin: \"query\",\nschema: { type: \"integer\",\nminimum: 1 },\ndescription: \"Page number (alternative to offset). Calculates offset as (page-1)*limit\" },\n {\n name: \"orderBy\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Sort field and direction. Accepts `field:asc` or `field:desc`, or a JSON array `[{\\\"field\\\":\\\"name\\\",\\\"direction\\\":\\\"asc\\\"}]`\",\n example: \"created_at:desc\"\n },\n {\n name: \"where\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"JSON object filter. Example: `{\\\"status\\\":[\\\"==\\\",\\\"active\\\"]}`\"\n },\n {\n name: \"include\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of relations to include (eager-load). Use `*` for all relations.\",\n example: \"author,tags\"\n },\n {\n name: \"fields\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of fields to return (field selection)\",\n example: \"id,name,created_at\"\n },\n {\n name: \"searchString\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Full-text search query\"\n },\n ...buildFilterParameters(collection)\n ],\n responses: {\n 200: {\n description: \"Paginated list of entities\",\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n data: {\n type: \"array\",\n items: { $ref: `#/components/schemas/${schemaName}` }\n },\n meta: { $ref: \"#/components/schemas/PaginationMeta\" }\n }\n }\n }\n }\n },\n ...errorResponses(requireAuth)\n }\n },\n post: {\n tags: [collection.name],\n summary: `Create ${collection.singularName || collection.name}`,\n operationId: `create${schemaName}`,\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}Input` }\n }\n }\n },\n responses: {\n 201: {\n description: \"Created entity\",\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}` }\n }\n }\n },\n ...errorResponses(requireAuth)\n }\n }\n };\n\n // ── GET/PUT/DELETE /data/{slug}/{id} ──────────────────────────\n const entityPath = `/data/${slug}/{id}`;\n paths[entityPath] = {\n get: {\n tags: [collection.name],\n summary: `Get ${collection.singularName || collection.name} by ID`,\n operationId: `get${schemaName}ById`,\n parameters: [\n { name: \"id\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: \"Entity ID\" },\n {\n name: \"include\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of relations to include\",\n example: \"author,tags\"\n }\n ],\n responses: {\n 200: {\n description: \"Entity found\",\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}` }\n }\n }\n },\n 404: { description: \"Entity not found\",\ncontent: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } } },\n ...errorResponses(requireAuth)\n }\n },\n put: {\n tags: [collection.name],\n summary: `Update ${collection.singularName || collection.name}`,\n operationId: `update${schemaName}`,\n parameters: [\n { name: \"id\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: \"Entity ID\" }\n ],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}Input` }\n }\n }\n },\n responses: {\n 200: {\n description: \"Updated entity\",\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}` }\n }\n }\n },\n 404: { description: \"Entity not found\",\ncontent: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } } },\n ...errorResponses(requireAuth)\n }\n },\n delete: {\n tags: [collection.name],\n summary: `Delete ${collection.singularName || collection.name}`,\n operationId: `delete${schemaName}`,\n parameters: [\n { name: \"id\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: \"Entity ID\" }\n ],\n responses: {\n 204: { description: \"Deleted successfully\" },\n 404: { description: \"Entity not found\",\ncontent: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } } },\n ...errorResponses(requireAuth)\n }\n }\n };\n\n // ── Subcollection routes ──────────────────────────────────────\n const relations = collection.relations;\n if (relations && relations.length > 0) {\n for (const relation of relations) {\n const relationName = relation.relationName;\n if (!relationName) continue;\n\n let targetName: string;\n try {\n const targetCollection = relation.target();\n targetName = targetCollection.singularName || targetCollection.name;\n } catch {\n targetName = relationName;\n }\n const targetSchema = toPascalCase(targetName);\n\n const subPath = `/data/${slug}/{parentId}/${relationName}`;\n\n // Only add if the schema exists (target collection is also registered)\n paths[subPath] = {\n get: {\n tags: [collection.name],\n summary: `List ${relationName} for ${withIndefiniteArticle(collection.singularName || collection.name)}`,\n operationId: `list${schemaName}${toPascalCase(relationName)}`,\n parameters: [\n { name: \"parentId\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: `${collection.singularName || collection.name} ID` },\n { name: \"limit\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 20 } },\n { name: \"offset\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 0 } },\n { name: \"orderBy\",\nin: \"query\",\nschema: { type: \"string\" } },\n { name: \"searchString\",\nin: \"query\",\nschema: { type: \"string\" } }\n ],\n responses: {\n 200: {\n description: `List of related ${relationName}`,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n data: {\n type: \"array\",\n items: schemas[targetSchema]\n ? { $ref: `#/components/schemas/${targetSchema}` }\n : { type: \"object\" }\n },\n meta: { $ref: \"#/components/schemas/PaginationMeta\" }\n }\n }\n }\n }\n },\n ...errorResponses(requireAuth)\n }\n }\n };\n }\n }\n }\n\n return spec;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────\n\n/**\n * Build the component schema for a collection (output / read shape).\n * All fields are included (including relation foreign keys).\n */\nfunction buildCollectionSchema(collection: CollectionConfig): Record<string, unknown> {\n const properties: Record<string, unknown> = {\n id: { type: \"string\",\ndescription: \"Unique identifier\" }\n };\n const required: string[] = [\"id\"];\n\n for (const [key, property] of Object.entries(collection.properties)) {\n // Skip relation properties — they are virtual and not part of the REST payload\n if (property.type === \"relation\") continue;\n\n properties[key] = convertPropertyToSchema(property);\n\n if (property.validation?.required) {\n required.push(key);\n }\n }\n\n return {\n type: \"object\",\n required: required.length > 0 ? required : undefined,\n properties\n };\n}\n\n/**\n * Build an input schema (for POST/PUT) — excludes auto-generated fields.\n */\nfunction buildCollectionInputSchema(collection: CollectionConfig): Record<string, unknown> {\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n\n for (const [key, property] of Object.entries(collection.properties)) {\n if (property.type === \"relation\") continue;\n\n // Skip auto-value date fields from the input schema\n if (property.type === \"date\" && property.autoValue) continue;\n\n // Skip auto-generated ID fields\n if (\"isId\" in property && property.isId && property.isId !== \"manual\" && property.isId !== true) continue;\n\n properties[key] = convertPropertyToSchema(property);\n\n if (property.validation?.required) {\n required.push(key);\n }\n }\n\n // Allow explicit ID for create (optional)\n properties[\"id\"] = {\n type: \"string\",\n description: \"Optional: client-assigned ID. If omitted, the server generates one.\"\n };\n\n return {\n type: \"object\",\n required: required.length > 0 ? required : undefined,\n properties\n };\n}\n\n/**\n * Convert a Rebase Property to an OpenAPI 3.0 schema object.\n */\nfunction convertPropertyToSchema(property: Property): Record<string, unknown> {\n const base: Record<string, unknown> = {};\n\n if (property.name) {\n base.description = property.name;\n }\n\n switch (property.type) {\n case \"string\": {\n const sp = property as StringProperty;\n base.type = \"string\";\n\n if (sp.enum) {\n const enumValues = resolveEnumValues(sp.enum);\n if (enumValues.length > 0) {\n base.enum = enumValues;\n }\n }\n\n if (sp.validation) {\n if (sp.validation.min !== undefined) base.minLength = sp.validation.min;\n if (sp.validation.max !== undefined) base.maxLength = sp.validation.max;\n if (sp.validation.length !== undefined) {\n base.minLength = sp.validation.length;\n base.maxLength = sp.validation.length;\n }\n if (sp.validation.matches !== undefined) {\n base.pattern = String(sp.validation.matches);\n }\n }\n\n if (sp.email) base.format = \"email\";\n if (sp.ui?.url) base.format = \"uri\";\n if (sp.storage) base.format = \"uri\";\n if (sp.ui?.markdown) base.description = (base.description || \"\") + \" (Markdown)\";\n\n return base;\n }\n\n case \"number\": {\n const np = property as NumberProperty;\n const isInteger = np.validation?.integer || np.columnType === \"integer\" || np.columnType === \"serial\" || np.columnType === \"bigserial\" || np.columnType === \"bigint\";\n base.type = isInteger ? \"integer\" : \"number\";\n\n if (np.enum) {\n const enumValues = resolveEnumValues(np.enum);\n if (enumValues.length > 0) {\n base.enum = enumValues;\n }\n }\n\n if (np.validation) {\n if (np.validation.min !== undefined) base.minimum = np.validation.min;\n if (np.validation.max !== undefined) base.maximum = np.validation.max;\n if (np.validation.moreThan !== undefined) {\n base.minimum = np.validation.moreThan;\n base.exclusiveMinimum = true;\n }\n if (np.validation.lessThan !== undefined) {\n base.maximum = np.validation.lessThan;\n base.exclusiveMaximum = true;\n }\n }\n\n return base;\n }\n\n case \"boolean\":\n base.type = \"boolean\";\n return base;\n\n case \"date\": {\n base.type = \"string\";\n if (property.mode === \"date\") {\n base.format = \"date\";\n } else {\n base.format = \"date-time\";\n }\n if (property.autoValue) {\n base.readOnly = true;\n base.description = (base.description || \"\") +\n (property.autoValue === \"on_create\" ? \" (Auto-set on creation)\" : \" (Auto-updated)\");\n }\n return base;\n }\n\n case \"geopoint\":\n base.type = \"object\";\n base.properties = {\n latitude: { type: \"number\" },\n longitude: { type: \"number\" }\n };\n base.required = [\"latitude\", \"longitude\"];\n return base;\n\n case \"reference\":\n base.type = \"string\";\n base.description = (base.description || \"\") + \" (Reference ID)\";\n return base;\n\n case \"array\": {\n const ap = property as ArrayProperty;\n base.type = \"array\";\n\n if (ap.oneOf) {\n // Discriminated union (e.g., content blocks)\n const typeField = ap.oneOf.typeField || \"type\";\n const valueField = ap.oneOf.valueField || \"value\";\n const variants: Record<string, unknown>[] = [];\n\n for (const [variantKey, variantProp] of Object.entries(ap.oneOf.properties)) {\n variants.push({\n type: \"object\",\n properties: {\n [typeField]: { type: \"string\",\nenum: [variantKey] },\n [valueField]: convertPropertyToSchema(variantProp)\n },\n required: [typeField, valueField]\n });\n }\n\n base.items = { oneOf: variants };\n } else if (ap.of) {\n if (Array.isArray(ap.of)) {\n base.items = { oneOf: ap.of.map(p => convertPropertyToSchema(p)) };\n } else {\n base.items = convertPropertyToSchema(ap.of);\n }\n } else {\n base.items = {};\n }\n\n if (ap.validation) {\n if (ap.validation.min !== undefined) base.minItems = ap.validation.min;\n if (ap.validation.max !== undefined) base.maxItems = ap.validation.max;\n }\n\n return base;\n }\n\n case \"map\": {\n const mp = property as MapProperty;\n base.type = \"object\";\n\n if (mp.properties) {\n const props: Record<string, unknown> = {};\n const req: string[] = [];\n\n for (const [key, subProp] of Object.entries(mp.properties)) {\n props[key] = convertPropertyToSchema(subProp);\n if (subProp.validation?.required) {\n req.push(key);\n }\n }\n\n base.properties = props;\n if (req.length > 0) base.required = req;\n } else if (mp.keyValue) {\n base.additionalProperties = true;\n }\n\n return base;\n }\n\n case \"vector\": {\n const vp = property as VectorProperty;\n base.type = \"array\";\n base.items = { type: \"number\" };\n base.description = (base.description || \"\") + ` (Vector(${vp.dimensions}))`;\n return base;\n }\n case \"binary\": {\n base.type = \"string\";\n base.description = (base.description || \"\") + \" (Binary/Base64)\";\n return base;\n }\n default:\n base.type = \"string\";\n return base;\n }\n}\n\n/**\n * Resolve EnumValues (array or record) into a flat array of enum values.\n */\nfunction resolveEnumValues(enumDef: Record<string | number, unknown> | Array<{ id: string | number }>): Array<string | number> {\n if (Array.isArray(enumDef)) {\n return enumDef.map(e => (typeof e === \"object\" && e !== null && \"id\" in e) ? e.id : e as string | number);\n }\n return Object.keys(enumDef).map(k => {\n // Preserve numeric keys as numbers\n const num = Number(k);\n return isNaN(num) ? k : num;\n });\n}\n\n/**\n * Build PostgREST-style filter parameters for a collection.\n * These are additional query parameters like `?status=eq.active&price=gte.100`.\n */\nfunction buildFilterParameters(collection: CollectionConfig): Array<Record<string, unknown>> {\n const params: Array<Record<string, unknown>> = [];\n\n for (const [key, property] of Object.entries(collection.properties)) {\n if (property.type === \"relation\" || property.type === \"map\" || property.type === \"array\" || property.type === \"geopoint\") {\n continue;\n }\n\n params.push({\n name: key,\n in: \"query\",\n required: false,\n schema: { type: \"string\" },\n description:\n `Filter by \\`${key}\\`. Supports PostgREST operators: ` +\n \"`eq.value`, `neq.value`, `gt.value`, `gte.value`, `lt.value`, `lte.value`, \" +\n \"`in.(a,b,c)`, `nin.(a,b,c)`, `cs.value` (array-contains), `csa.(a,b)` (array-contains-any). \" +\n \"Plain values imply equality.\",\n example: property.type === \"string\" ? \"eq.active\" : property.type === \"number\" ? \"gte.100\" : undefined\n });\n }\n\n return params;\n}\n\n/**\n * Standard error responses included on every endpoint.\n */\nfunction errorResponses(requireAuth: boolean): Record<string, unknown> {\n const responses: Record<string, unknown> = {\n 400: {\n description: \"Bad request\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n },\n 500: {\n description: \"Internal server error\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n }\n };\n\n if (requireAuth) {\n responses[401] = {\n description: \"Authentication required or invalid token\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n };\n responses[403] = {\n description: \"Insufficient permissions\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n };\n }\n\n return responses;\n}\n\n/**\n * Prefix a noun with \"a\" or \"an\" based on its leading sound.\n */\nfunction withIndefiniteArticle(noun: string): string {\n return `${/^[aeiou]/i.test(noun) ? \"an\" : \"a\"} ${noun}`;\n}\n\n/**\n * Convert a string to PascalCase for schema names.\n */\nfunction toPascalCase(str: string): string {\n return str\n .replace(/[^a-zA-Z0-9]+/g, \" \")\n .split(\" \")\n .filter(Boolean)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"openapi-generator-C3_pTcsb.js","names":[],"sources":["../src/api/openapi-generator.ts"],"sourcesContent":["import { CollectionConfig, Property, StringProperty, NumberProperty, ArrayProperty, MapProperty, Relation, VectorProperty } from \"@rebasepro/types\";\n\n/**\n * OpenAPI 3.0.3 specification generator.\n *\n * Produces a spec that exactly mirrors the REST API consumed by the\n * Rebase SDK client (`@rebasepro/client`).\n *\n * Routes are mounted at `{basePath}/data/{slug}` by `initializeRebaseBackend`.\n */\n\nexport interface OpenApiGeneratorOptions {\n /** Base path for the API (e.g. \"/api\"). Defaults to \"/api\". */\n basePath?: string;\n /** Whether auth is enabled on data routes. Defaults to true. */\n requireAuth?: boolean;\n}\n\nexport function generateOpenApiSpec(\n collections: CollectionConfig[],\n options: OpenApiGeneratorOptions = {}\n): Record<string, unknown> {\n const basePath = options.basePath ?? \"/api\";\n const requireAuth = options.requireAuth ?? true;\n\n const spec: Record<string, unknown> = {\n openapi: \"3.0.3\",\n info: {\n title: \"Rebase API\",\n version: \"1.0.0\",\n description:\n \"Auto-generated REST API from Rebase collection definitions. \" +\n \"This is the same API consumed by the `@rebasepro/client` SDK.\"\n },\n servers: [\n {\n url: basePath,\n description: \"API Server\"\n }\n ],\n paths: {} as Record<string, unknown>,\n components: {\n schemas: {\n ErrorResponse: {\n type: \"object\",\n properties: {\n error: {\n type: \"object\",\n required: [\"message\", \"code\"],\n properties: {\n message: { type: \"string\" },\n code: { type: \"string\" },\n details: {}\n }\n }\n }\n },\n PaginationMeta: {\n type: \"object\",\n properties: {\n total: { type: \"integer\",\ndescription: \"Total number of matching records\" },\n limit: { type: \"integer\",\ndescription: \"Page size used for this query\" },\n offset: { type: \"integer\",\ndescription: \"Number of records skipped\" },\n hasMore: { type: \"boolean\",\ndescription: \"Whether more records exist beyond this page\" }\n }\n }\n } as Record<string, unknown>,\n securitySchemes: {} as Record<string, unknown>\n },\n tags: [] as Array<{ name: string; description?: string }>\n };\n\n // ── Security Schemes ─────────────────────────────────────────────────\n if (requireAuth) {\n (spec.components as Record<string, unknown>).securitySchemes = {\n bearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n description:\n \"JWT access token obtained from `POST /auth/login` or `POST /auth/register`. \" +\n \"Can also be a static service key for server-to-server authentication.\"\n },\n queryToken: {\n type: \"apiKey\",\n in: \"query\",\n name: \"token\",\n description: \"Alternative: pass the JWT or service key as a `token` query parameter.\"\n }\n };\n (spec as Record<string, unknown>).security = [\n { bearerAuth: [] },\n { queryToken: [] }\n ];\n }\n\n const paths = spec.paths as Record<string, unknown>;\n const schemas = (spec.components as Record<string, unknown>).schemas as Record<string, unknown>;\n const tags = spec.tags as Array<{ name: string; description?: string }>;\n\n // ── Collection routes ────────────────────────────────────────────────\n for (const collection of (collections || [])) {\n const schemaName = toPascalCase(collection.singularName || collection.name);\n const slug = collection.slug;\n\n tags.push({\n name: collection.name,\n description: collection.description || `CRUD operations for ${collection.name}`\n });\n\n // Build component schema for this collection\n schemas[schemaName] = buildCollectionSchema(collection);\n\n // Build an \"input\" schema (no read-only/auto fields like autoValue dates)\n schemas[`${schemaName}Input`] = buildCollectionInputSchema(collection);\n\n const dataPath = `/data/${slug}`;\n\n // ── GET /data/{slug} — List entities ──────────────────────────\n paths[dataPath] = {\n get: {\n tags: [collection.name],\n summary: `List ${collection.name}`,\n operationId: `list${schemaName}`,\n parameters: [\n { name: \"limit\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 20,\nmaximum: 100 },\ndescription: \"Maximum number of records to return\" },\n { name: \"offset\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 0 },\ndescription: \"Number of records to skip\" },\n { name: \"page\",\nin: \"query\",\nschema: { type: \"integer\",\nminimum: 1 },\ndescription: \"Page number (alternative to offset). Calculates offset as (page-1)*limit\" },\n {\n name: \"orderBy\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Sort field and direction. Accepts `field:asc` or `field:desc`, or a JSON array `[{\\\"field\\\":\\\"name\\\",\\\"direction\\\":\\\"asc\\\"}]`\",\n example: \"created_at:desc\"\n },\n {\n name: \"where\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"JSON object filter. Example: `{\\\"status\\\":[\\\"==\\\",\\\"active\\\"]}`\"\n },\n {\n name: \"include\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of relations to include (eager-load). Use `*` for all relations.\",\n example: \"author,tags\"\n },\n {\n name: \"fields\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of fields to return (field selection)\",\n example: \"id,name,created_at\"\n },\n {\n name: \"searchString\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Full-text search query\"\n },\n ...buildFilterParameters(collection)\n ],\n responses: {\n 200: {\n description: \"Paginated list of entities\",\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n data: {\n type: \"array\",\n items: { $ref: `#/components/schemas/${schemaName}` }\n },\n meta: { $ref: \"#/components/schemas/PaginationMeta\" }\n }\n }\n }\n }\n },\n ...errorResponses(requireAuth)\n }\n },\n post: {\n tags: [collection.name],\n summary: `Create ${collection.singularName || collection.name}`,\n operationId: `create${schemaName}`,\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}Input` }\n }\n }\n },\n responses: {\n 201: {\n description: \"Created entity\",\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}` }\n }\n }\n },\n ...errorResponses(requireAuth)\n }\n }\n };\n\n // ── GET/PUT/DELETE /data/{slug}/{id} ──────────────────────────\n const entityPath = `/data/${slug}/{id}`;\n paths[entityPath] = {\n get: {\n tags: [collection.name],\n summary: `Get ${collection.singularName || collection.name} by ID`,\n operationId: `get${schemaName}ById`,\n parameters: [\n { name: \"id\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: \"Entity ID\" },\n {\n name: \"include\",\n in: \"query\",\n schema: { type: \"string\" },\n description: \"Comma-separated list of relations to include\",\n example: \"author,tags\"\n }\n ],\n responses: {\n 200: {\n description: \"Entity found\",\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}` }\n }\n }\n },\n 404: { description: \"Entity not found\",\ncontent: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } } },\n ...errorResponses(requireAuth)\n }\n },\n put: {\n tags: [collection.name],\n summary: `Update ${collection.singularName || collection.name}`,\n operationId: `update${schemaName}`,\n parameters: [\n { name: \"id\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: \"Entity ID\" }\n ],\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}Input` }\n }\n }\n },\n responses: {\n 200: {\n description: \"Updated entity\",\n content: {\n \"application/json\": {\n schema: { $ref: `#/components/schemas/${schemaName}` }\n }\n }\n },\n 404: { description: \"Entity not found\",\ncontent: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } } },\n ...errorResponses(requireAuth)\n }\n },\n delete: {\n tags: [collection.name],\n summary: `Delete ${collection.singularName || collection.name}`,\n operationId: `delete${schemaName}`,\n parameters: [\n { name: \"id\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: \"Entity ID\" }\n ],\n responses: {\n 204: { description: \"Deleted successfully\" },\n 404: { description: \"Entity not found\",\ncontent: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } } },\n ...errorResponses(requireAuth)\n }\n }\n };\n\n // ── Subcollection routes ──────────────────────────────────────\n const relations = collection.relations;\n if (relations && relations.length > 0) {\n for (const relation of relations) {\n const relationName = relation.relationName;\n if (!relationName) continue;\n\n let targetName: string;\n try {\n const targetCollection = relation.target();\n targetName = targetCollection.singularName || targetCollection.name;\n } catch {\n targetName = relationName;\n }\n const targetSchema = toPascalCase(targetName);\n\n const subPath = `/data/${slug}/{parentId}/${relationName}`;\n\n // Only add if the schema exists (target collection is also registered)\n paths[subPath] = {\n get: {\n tags: [collection.name],\n summary: `List ${relationName} for ${withIndefiniteArticle(collection.singularName || collection.name)}`,\n operationId: `list${schemaName}${toPascalCase(relationName)}`,\n parameters: [\n { name: \"parentId\",\nin: \"path\",\nrequired: true,\nschema: { type: \"string\" },\ndescription: `${collection.singularName || collection.name} ID` },\n { name: \"limit\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 20 } },\n { name: \"offset\",\nin: \"query\",\nschema: { type: \"integer\",\ndefault: 0 } },\n { name: \"orderBy\",\nin: \"query\",\nschema: { type: \"string\" } },\n { name: \"searchString\",\nin: \"query\",\nschema: { type: \"string\" } }\n ],\n responses: {\n 200: {\n description: `List of related ${relationName}`,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n data: {\n type: \"array\",\n items: schemas[targetSchema]\n ? { $ref: `#/components/schemas/${targetSchema}` }\n : { type: \"object\" }\n },\n meta: { $ref: \"#/components/schemas/PaginationMeta\" }\n }\n }\n }\n }\n },\n ...errorResponses(requireAuth)\n }\n }\n };\n }\n }\n }\n\n return spec;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────\n\n/**\n * Build the component schema for a collection (output / read shape).\n * All fields are included (including relation foreign keys).\n */\nfunction buildCollectionSchema(collection: CollectionConfig): Record<string, unknown> {\n const properties: Record<string, unknown> = {\n id: { type: \"string\",\ndescription: \"Unique identifier\" }\n };\n const required: string[] = [\"id\"];\n\n for (const [key, property] of Object.entries(collection.properties)) {\n // Skip relation properties — they are virtual and not part of the REST payload\n if (property.type === \"relation\") continue;\n\n properties[key] = convertPropertyToSchema(property);\n\n if (property.validation?.required) {\n required.push(key);\n }\n }\n\n return {\n type: \"object\",\n required: required.length > 0 ? required : undefined,\n properties\n };\n}\n\n/**\n * Build an input schema (for POST/PUT) — excludes auto-generated fields.\n */\nfunction buildCollectionInputSchema(collection: CollectionConfig): Record<string, unknown> {\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n\n for (const [key, property] of Object.entries(collection.properties)) {\n if (property.type === \"relation\") continue;\n\n // Skip auto-value date fields from the input schema\n if (property.type === \"date\" && property.autoValue) continue;\n\n // Skip auto-generated ID fields\n if (\"isId\" in property && property.isId && property.isId !== \"manual\" && property.isId !== true) continue;\n\n properties[key] = convertPropertyToSchema(property);\n\n if (property.validation?.required) {\n required.push(key);\n }\n }\n\n // Allow explicit ID for create (optional)\n properties[\"id\"] = {\n type: \"string\",\n description: \"Optional: client-assigned ID. If omitted, the server generates one.\"\n };\n\n return {\n type: \"object\",\n required: required.length > 0 ? required : undefined,\n properties\n };\n}\n\n/**\n * Convert a Rebase Property to an OpenAPI 3.0 schema object.\n */\nfunction convertPropertyToSchema(property: Property): Record<string, unknown> {\n const base: Record<string, unknown> = {};\n\n if (property.name) {\n base.description = property.name;\n }\n\n switch (property.type) {\n case \"string\": {\n const sp = property as StringProperty;\n base.type = \"string\";\n\n if (sp.enum) {\n const enumValues = resolveEnumValues(sp.enum);\n if (enumValues.length > 0) {\n base.enum = enumValues;\n }\n }\n\n if (sp.validation) {\n if (sp.validation.min !== undefined) base.minLength = sp.validation.min;\n if (sp.validation.max !== undefined) base.maxLength = sp.validation.max;\n if (sp.validation.length !== undefined) {\n base.minLength = sp.validation.length;\n base.maxLength = sp.validation.length;\n }\n if (sp.validation.matches !== undefined) {\n base.pattern = String(sp.validation.matches);\n }\n }\n\n if (sp.email) base.format = \"email\";\n if (sp.url) base.format = \"uri\";\n if (sp.storage) base.format = \"uri\";\n\n return base;\n }\n\n case \"number\": {\n const np = property as NumberProperty;\n const isInteger = np.validation?.integer || np.columnType === \"integer\" || np.columnType === \"serial\" || np.columnType === \"bigserial\" || np.columnType === \"bigint\";\n base.type = isInteger ? \"integer\" : \"number\";\n\n if (np.enum) {\n const enumValues = resolveEnumValues(np.enum);\n if (enumValues.length > 0) {\n base.enum = enumValues;\n }\n }\n\n if (np.validation) {\n if (np.validation.min !== undefined) base.minimum = np.validation.min;\n if (np.validation.max !== undefined) base.maximum = np.validation.max;\n if (np.validation.moreThan !== undefined) {\n base.minimum = np.validation.moreThan;\n base.exclusiveMinimum = true;\n }\n if (np.validation.lessThan !== undefined) {\n base.maximum = np.validation.lessThan;\n base.exclusiveMaximum = true;\n }\n }\n\n return base;\n }\n\n case \"boolean\":\n base.type = \"boolean\";\n return base;\n\n case \"date\": {\n base.type = \"string\";\n if (property.mode === \"date\") {\n base.format = \"date\";\n } else {\n base.format = \"date-time\";\n }\n if (property.autoValue) {\n base.readOnly = true;\n base.description = (base.description || \"\") +\n (property.autoValue === \"on_create\" ? \" (Auto-set on creation)\" : \" (Auto-updated)\");\n }\n return base;\n }\n\n case \"geopoint\":\n base.type = \"object\";\n base.properties = {\n latitude: { type: \"number\" },\n longitude: { type: \"number\" }\n };\n base.required = [\"latitude\", \"longitude\"];\n return base;\n\n case \"reference\":\n base.type = \"string\";\n base.description = (base.description || \"\") + \" (Reference ID)\";\n return base;\n\n case \"array\": {\n const ap = property as ArrayProperty;\n base.type = \"array\";\n\n if (ap.oneOf) {\n // Discriminated union (e.g., content blocks)\n const typeField = ap.oneOf.typeField || \"type\";\n const valueField = ap.oneOf.valueField || \"value\";\n const variants: Record<string, unknown>[] = [];\n\n for (const [variantKey, variantProp] of Object.entries(ap.oneOf.properties)) {\n variants.push({\n type: \"object\",\n properties: {\n [typeField]: { type: \"string\",\nenum: [variantKey] },\n [valueField]: convertPropertyToSchema(variantProp)\n },\n required: [typeField, valueField]\n });\n }\n\n base.items = { oneOf: variants };\n } else if (ap.of) {\n if (Array.isArray(ap.of)) {\n base.items = { oneOf: ap.of.map(p => convertPropertyToSchema(p)) };\n } else {\n base.items = convertPropertyToSchema(ap.of);\n }\n } else {\n base.items = {};\n }\n\n if (ap.validation) {\n if (ap.validation.min !== undefined) base.minItems = ap.validation.min;\n if (ap.validation.max !== undefined) base.maxItems = ap.validation.max;\n }\n\n return base;\n }\n\n case \"map\": {\n const mp = property as MapProperty;\n base.type = \"object\";\n\n if (mp.properties) {\n const props: Record<string, unknown> = {};\n const req: string[] = [];\n\n for (const [key, subProp] of Object.entries(mp.properties)) {\n props[key] = convertPropertyToSchema(subProp);\n if (subProp.validation?.required) {\n req.push(key);\n }\n }\n\n base.properties = props;\n if (req.length > 0) base.required = req;\n } else if (mp.keyValue) {\n base.additionalProperties = true;\n }\n\n return base;\n }\n\n case \"vector\": {\n const vp = property as VectorProperty;\n base.type = \"array\";\n base.items = { type: \"number\" };\n base.description = (base.description || \"\") + ` (Vector(${vp.dimensions}))`;\n return base;\n }\n case \"binary\": {\n base.type = \"string\";\n base.description = (base.description || \"\") + \" (Binary/Base64)\";\n return base;\n }\n default:\n base.type = \"string\";\n return base;\n }\n}\n\n/**\n * Resolve EnumValues (array or record) into a flat array of enum values.\n */\nfunction resolveEnumValues(enumDef: Record<string | number, unknown> | Array<{ id: string | number }>): Array<string | number> {\n if (Array.isArray(enumDef)) {\n return enumDef.map(e => (typeof e === \"object\" && e !== null && \"id\" in e) ? e.id : e as string | number);\n }\n return Object.keys(enumDef).map(k => {\n // Preserve numeric keys as numbers\n const num = Number(k);\n return isNaN(num) ? k : num;\n });\n}\n\n/**\n * Build PostgREST-style filter parameters for a collection.\n * These are additional query parameters like `?status=eq.active&price=gte.100`.\n */\nfunction buildFilterParameters(collection: CollectionConfig): Array<Record<string, unknown>> {\n const params: Array<Record<string, unknown>> = [];\n\n for (const [key, property] of Object.entries(collection.properties)) {\n if (property.type === \"relation\" || property.type === \"map\" || property.type === \"array\" || property.type === \"geopoint\") {\n continue;\n }\n\n params.push({\n name: key,\n in: \"query\",\n required: false,\n schema: { type: \"string\" },\n description:\n `Filter by \\`${key}\\`. Supports PostgREST operators: ` +\n \"`eq.value`, `neq.value`, `gt.value`, `gte.value`, `lt.value`, `lte.value`, \" +\n \"`in.(a,b,c)`, `nin.(a,b,c)`, `cs.value` (array-contains), `csa.(a,b)` (array-contains-any). \" +\n \"Plain values imply equality.\",\n example: property.type === \"string\" ? \"eq.active\" : property.type === \"number\" ? \"gte.100\" : undefined\n });\n }\n\n return params;\n}\n\n/**\n * Standard error responses included on every endpoint.\n */\nfunction errorResponses(requireAuth: boolean): Record<string, unknown> {\n const responses: Record<string, unknown> = {\n 400: {\n description: \"Bad request\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n },\n 500: {\n description: \"Internal server error\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n }\n };\n\n if (requireAuth) {\n responses[401] = {\n description: \"Authentication required or invalid token\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n };\n responses[403] = {\n description: \"Insufficient permissions\",\n content: { \"application/json\": { schema: { $ref: \"#/components/schemas/ErrorResponse\" } } }\n };\n }\n\n return responses;\n}\n\n/**\n * Prefix a noun with \"a\" or \"an\" based on its leading sound.\n */\nfunction withIndefiniteArticle(noun: string): string {\n return `${/^[aeiou]/i.test(noun) ? \"an\" : \"a\"} ${noun}`;\n}\n\n/**\n * Convert a string to PascalCase for schema names.\n */\nfunction toPascalCase(str: string): string {\n return str\n .replace(/[^a-zA-Z0-9]+/g, \" \")\n .split(\" \")\n .filter(Boolean)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n"],"mappings":"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"}
@@ -1,12 +1,39 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  import "process";
3
3
  __createRequire(import.meta.url);
4
+ import { f as ADMIN_COLLECTION_KEYS } from "./src-DRUswG_w.js";
4
5
  import { errorHandler } from "./index.es.js";
5
6
  import * as fs$1 from "fs";
6
7
  import * as path$1 from "path";
7
8
  import { Hono } from "hono";
8
9
  import { IndentationText, Project, SyntaxKind } from "ts-morph";
9
10
  //#region src/api/ast-schema-editor.ts
11
+ /**
12
+ * Move presentation keys into the `admin` block.
13
+ *
14
+ * `ADMIN_COLLECTION_KEYS` comes from `@rebasepro/types` rather than being spelled
15
+ * out here, so adding a field to `AdminCollectionOptions` cannot leave this writer
16
+ * behind. Any such key already inside `admin` wins over a top-level copy: the
17
+ * nested one is what the file said, and a flat duplicate is the view model's
18
+ * flattening leaking back.
19
+ */
20
+ function nestAdminKeys(collectionData) {
21
+ const adminKeys = new Set(ADMIN_COLLECTION_KEYS);
22
+ const existingBlock = collectionData.admin ?? {};
23
+ const top = {};
24
+ const block = {};
25
+ for (const [key, value] of Object.entries(collectionData)) {
26
+ if (key === "admin") continue;
27
+ if (adminKeys.has(key)) block[key] = value;
28
+ else top[key] = value;
29
+ }
30
+ const merged = {
31
+ ...block,
32
+ ...existingBlock
33
+ };
34
+ if (Object.keys(merged).length > 0) top.admin = merged;
35
+ return top;
36
+ }
10
37
  var AstSchemaEditor = class {
11
38
  project;
12
39
  collectionsDir;
@@ -157,13 +184,14 @@ var AstSchemaEditor = class {
157
184
  if (!file || !collectionObj) {
158
185
  const safeId = this.sanitizeCollectionId(collectionId);
159
186
  const newFilePath = this.safePath(`${safeId}.ts`);
160
- file = this.project.createSourceFile(newFilePath, `import { CollectionConfig } from "@rebasepro/types";\n\nconst ${safeId}Collection: CollectionConfig = ${this.convertJsonToAstString(collectionData)};\n\nexport default ${safeId}Collection;\n`, { overwrite: true });
187
+ file = this.project.createSourceFile(newFilePath, `import { CollectionConfig } from "@rebasepro/types";\n\nconst ${safeId}Collection: CollectionConfig = ${this.convertJsonToAstString(nestAdminKeys(collectionData))};\n\nexport default ${safeId}Collection;\n`, { overwrite: true });
161
188
  } else {
162
189
  if (!("securityRules" in collectionData) || collectionData.securityRules === void 0 || Array.isArray(collectionData.securityRules) && collectionData.securityRules.length === 0) {
163
190
  const srProp = collectionObj.getProperty("securityRules");
164
191
  if (srProp) srProp.remove();
165
192
  delete collectionData["securityRules"];
166
193
  }
194
+ collectionData = nestAdminKeys(collectionData);
167
195
  for (const key of Object.keys(collectionData)) {
168
196
  if (key === "relations") continue;
169
197
  const prop = collectionObj.getProperty(key);
@@ -216,4 +244,4 @@ function createSchemaEditorRoutes(collectionsDir) {
216
244
  //#endregion
217
245
  export { createSchemaEditorRoutes };
218
246
 
219
- //# sourceMappingURL=schema-editor-routes-D3ef8zu1.js.map
247
+ //# sourceMappingURL=schema-editor-routes-CF-g0gVN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-editor-routes-CF-g0gVN.js","names":[],"sources":["../src/api/ast-schema-editor.ts","../src/api/schema-editor-routes.ts"],"sourcesContent":["import { Project, SyntaxKind, ObjectLiteralExpression, ObjectLiteralElementLike, PropertyAssignment, VariableDeclaration, IndentationText } from \"ts-morph\";\nimport { ADMIN_COLLECTION_KEYS } from \"@rebasepro/types\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\n\n/**\n * Move presentation keys into the `admin` block.\n *\n * `ADMIN_COLLECTION_KEYS` comes from `@rebasepro/types` rather than being spelled\n * out here, so adding a field to `AdminCollectionOptions` cannot leave this writer\n * behind. Any such key already inside `admin` wins over a top-level copy: the\n * nested one is what the file said, and a flat duplicate is the view model's\n * flattening leaking back.\n */\nexport function nestAdminKeys(collectionData: Record<string, unknown>): Record<string, unknown> {\n const adminKeys = new Set<string>(ADMIN_COLLECTION_KEYS as readonly string[]);\n const existingBlock = (collectionData.admin ?? {}) as Record<string, unknown>;\n\n const top: Record<string, unknown> = {};\n const block: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(collectionData)) {\n if (key === \"admin\") continue;\n if (adminKeys.has(key)) block[key] = value;\n else top[key] = value;\n }\n\n const merged = { ...block, ...existingBlock };\n if (Object.keys(merged).length > 0) top.admin = merged;\n return top;\n}\n\nexport class AstSchemaEditor {\n private project: Project;\n private collectionsDir: string;\n\n constructor(collectionsDir: string) {\n this.project = new Project({\n manipulationSettings: {\n indentationText: IndentationText.FourSpaces\n }\n });\n if (fs.existsSync(collectionsDir)) {\n this.project.addSourceFilesAtPaths(`${collectionsDir}/**/*.ts`);\n }\n this.collectionsDir = path.resolve(collectionsDir);\n }\n\n /**\n * Sanitize collectionId to prevent path traversal attacks.\n * Only allows alphanumeric characters, underscores, and hyphens.\n */\n private sanitizeCollectionId(collectionId: string): string {\n const sanitized = collectionId.replace(/[^a-zA-Z0-9_-]/g, \"\");\n if (!sanitized || sanitized !== collectionId) {\n throw new Error(`Invalid collection ID: \"${collectionId}\". Only alphanumeric characters, underscores, and hyphens are allowed.`);\n }\n return sanitized;\n }\n\n /**\n * Resolve a file path and ensure it falls within the collectionsDir.\n */\n private safePath(filename: string): string {\n const resolved = path.resolve(this.collectionsDir, filename);\n if (!resolved.startsWith(this.collectionsDir + path.sep) && resolved !== this.collectionsDir) {\n throw new Error(\"Path traversal detected: resolved path is outside the collections directory.\");\n }\n return resolved;\n }\n\n private getCollectionFile(collectionId: string) {\n const safeId = this.sanitizeCollectionId(collectionId);\n const filePath = this.safePath(`${safeId}.ts`);\n let file = this.project.getSourceFile(filePath);\n if (!file && fs.existsSync(filePath)) {\n this.project.addSourceFilesAtPaths(`${this.collectionsDir}/**/*.ts`);\n file = this.project.getSourceFile(filePath);\n }\n return file;\n }\n\n private getCollectionObject(collectionId: string): ObjectLiteralExpression | null {\n const file = this.getCollectionFile(collectionId);\n if (!file) return null;\n\n const defaultExport = file.getDefaultExportSymbol();\n if (defaultExport) {\n const declaration = defaultExport.getDeclarations()[0];\n if (declaration && declaration.getKind() === SyntaxKind.ExportAssignment) {\n const expr = declaration.asKind(SyntaxKind.ExportAssignment)?.getExpression();\n if (expr && expr.getKind() === SyntaxKind.Identifier) {\n const varName = expr.getText();\n const varDecl = file.getVariableDeclaration(varName);\n return varDecl?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression) || null;\n }\n }\n }\n // Fallback: Just get the first exported VariableDeclaration with an ObjectLiteral\n const varDecls = file.getVariableDeclarations();\n for (const varDecl of varDecls) {\n const init = varDecl.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n if (init) return init;\n }\n return null;\n }\n\n private convertJsonToAstString(obj: unknown, indentLevel = 0, oldAstNode?: ObjectLiteralExpression): string {\n // Base TS-morph parses arrays as 2 levels deep from the property key:\n // PropertiesObject = level 1, PropertyConfig = level 2.\n // We calibrate the spacing multiples to keep the items flush with standard TS format.\n const indentStr = \" \";\n const indent = indentStr.repeat(indentLevel);\n const innerIndent = indentStr.repeat(indentLevel + 1);\n\n if (obj === null || obj === undefined) {\n return \"undefined\";\n }\n if (typeof obj === \"string\") {\n return JSON.stringify(obj);\n }\n if (typeof obj === \"number\" || typeof obj === \"boolean\") {\n return String(obj);\n }\n if (Array.isArray(obj)) {\n if (obj.length === 0) return \"[]\";\n const items = obj.map(item => this.convertJsonToAstString(item, indentLevel + 1));\n return `[\\n${innerIndent}${items.join(`,\\n${innerIndent}`)}\\n${indent}]`;\n }\n if (typeof obj === \"object\") {\n const record = obj as Record<string, unknown>;\n const keys = Object.keys(record);\n\n // Collect preserved AST properties\n const preservedProps: string[] = [];\n if (oldAstNode) {\n const oldProps = oldAstNode.getProperties();\n for (const oldProp of oldProps) {\n if (oldProp.isKind(SyntaxKind.PropertyAssignment)) {\n const nameNode = oldProp.getNameNode();\n let name = nameNode.getText();\n if (name.startsWith('\"') && name.endsWith('\"')) name = name.slice(1, -1);\n if (name.startsWith(\"'\") && name.endsWith(\"'\")) name = name.slice(1, -1);\n\n // If the JSON object doesn't have this key, check if we should preserve it\n if (!(name in record)) {\n const init = oldProp.getInitializer();\n if (init) {\n const kind = init.getKind();\n const isCode = kind === SyntaxKind.ArrowFunction ||\n kind === SyntaxKind.FunctionExpression ||\n kind === SyntaxKind.Identifier ||\n kind === SyntaxKind.CallExpression ||\n kind === SyntaxKind.JsxElement;\n\n if (isCode || name === \"target\" || name === \"callbacks\" || name === \"permissions\" || name === \"securityRules\") {\n // Preserve this property exactly as it was\n const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);\n preservedProps.push(`${keyStr}: ${init.getText()}`);\n }\n }\n }\n }\n }\n }\n\n if (keys.length === 0 && preservedProps.length === 0) return \"{}\";\n\n const props = keys.map(key => {\n const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);\n\n // If the value is an object, pass the old AST node to recurse\n let childAstNode: ObjectLiteralExpression | undefined;\n if (oldAstNode && typeof record[key] === \"object\" && record[key] !== null && !Array.isArray(record[key])) {\n const oldProp = oldAstNode.getProperty(\n (p: ObjectLiteralElementLike) => \"getName\" in p && typeof (p as PropertyAssignment).getName === \"function\" && ((p as PropertyAssignment).getName() === key || (p as PropertyAssignment).getName() === `\"${key}\"` || (p as PropertyAssignment).getName() === `'${key}'`)\n );\n if (oldProp && oldProp.isKind(SyntaxKind.PropertyAssignment)) {\n childAstNode = oldProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n }\n }\n\n return `${keyStr}: ${this.convertJsonToAstString(record[key], indentLevel + 1, childAstNode)}`;\n });\n\n const allProps = [...props, ...preservedProps];\n return `{\\n${innerIndent}${allProps.join(`,\\n${innerIndent}`)}\\n${indent}}`;\n }\n return \"undefined\";\n }\n\n public async saveProperty(collectionId: string, propertyKey: string, propertyConfig: Record<string, unknown>) {\n const collectionObj = this.getCollectionObject(collectionId);\n if (!collectionObj) throw new Error(`Collection ${collectionId} not found in ATS workspace.`);\n\n let propertiesProp = collectionObj.getProperty(\"properties\") as PropertyAssignment;\n if (!propertiesProp) {\n propertiesProp = collectionObj.addPropertyAssignment({\n name: \"properties\",\n initializer: \"{}\"\n });\n }\n\n const propsObj = propertiesProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n if (propsObj) {\n const existingProp = propsObj.getProperty(\n (p: ObjectLiteralElementLike) => \"getName\" in p && typeof (p as PropertyAssignment).getName === \"function\" && ((p as PropertyAssignment).getName() === propertyKey || (p as PropertyAssignment).getName() === `\"${propertyKey}\"`)\n );\n\n let oldPropAstNode: ObjectLiteralExpression | undefined;\n if (existingProp && existingProp.isKind(SyntaxKind.PropertyAssignment)) {\n oldPropAstNode = existingProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n }\n\n const newInitializer = this.convertJsonToAstString(propertyConfig, 2, oldPropAstNode);\n\n if (existingProp) {\n if (existingProp.isKind(SyntaxKind.PropertyAssignment)) {\n existingProp.setInitializer(newInitializer);\n }\n } else {\n propsObj.addPropertyAssignment({\n name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),\n initializer: newInitializer\n });\n }\n\n const file = this.getCollectionFile(collectionId);\n if (file) {\n file.formatText();\n }\n await this.project.save();\n }\n }\n\n public async deleteProperty(collectionId: string, propertyKey: string) {\n const collectionObj = this.getCollectionObject(collectionId);\n if (!collectionObj) return;\n\n const propertiesProp = collectionObj.getProperty(\"properties\") as PropertyAssignment;\n if (propertiesProp) {\n const propsObj = propertiesProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n if (propsObj) {\n const existingProp = propsObj.getProperty(\n (p: ObjectLiteralElementLike) => \"getName\" in p && typeof (p as PropertyAssignment).getName === \"function\" && ((p as PropertyAssignment).getName() === propertyKey || (p as PropertyAssignment).getName() === `\"${propertyKey}\"`)\n );\n if (existingProp) {\n existingProp.remove();\n const file = this.getCollectionFile(collectionId);\n if (file) {\n file.formatText();\n }\n await this.project.save();\n }\n }\n }\n }\n\n public async saveCollection(collectionId: string, collectionData: Record<string, unknown>) {\n let file = this.getCollectionFile(collectionId);\n const collectionObj = this.getCollectionObject(collectionId);\n\n if (!file || !collectionObj) {\n // Create a new file\n const safeId = this.sanitizeCollectionId(collectionId);\n const newFilePath = this.safePath(`${safeId}.ts`);\n file = this.project.createSourceFile(newFilePath, `import { CollectionConfig } from \"@rebasepro/types\";\\n\\nconst ${safeId}Collection: CollectionConfig = ${this.convertJsonToAstString(nestAdminKeys(collectionData))};\\n\\nexport default ${safeId}Collection;\\n`, { overwrite: true });\n } else {\n // Update root level properties gracefully\n\n // Force delete securityRules if empty or undefined to handle Formex / serialization stripping\n if (!(\"securityRules\" in collectionData) || collectionData.securityRules === undefined || (Array.isArray(collectionData.securityRules) && collectionData.securityRules.length === 0)) {\n const srProp = collectionObj.getProperty(\"securityRules\");\n if (srProp) {\n srProp.remove();\n }\n\n // If it was in collectionData as an empty array, delete it so the loop below doesn't add it back as \"[]\"\n // Actually, if it's \"[]\", omitting it entirely from the TS file achieves the same logical effect (no RLS rules)\n // and correctly triggers \"unmapped policies\" if the DB still has them.\n delete collectionData[\"securityRules\"];\n }\n\n // The panel works with a flat view model — presentation merged onto the\n // collection — so what arrives here has `icon` and `listProperties` at\n // the top level. On disk they belong inside `admin`. Writing them flat\n // would produce a file the backend loads and ignores and the panel\n // never reads back, which looks exactly like the edit not saving.\n collectionData = nestAdminKeys(collectionData);\n\n for (const key of Object.keys(collectionData)) {\n if (key === \"relations\") continue; // Kept via other AST functions or handled separately.\n\n const prop = collectionObj.getProperty(key) as PropertyAssignment;\n\n let oldAstNode: ObjectLiteralExpression | undefined;\n if (prop && prop.isKind(SyntaxKind.PropertyAssignment)) {\n oldAstNode = prop.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);\n }\n\n const newInit = this.convertJsonToAstString(collectionData[key], 1, oldAstNode);\n if (prop) {\n prop.setInitializer(newInit);\n } else {\n collectionObj.addPropertyAssignment({\n name: key,\n initializer: newInit\n });\n }\n }\n }\n if (file) {\n file.formatText();\n }\n await this.project.save();\n }\n\n public async deleteCollection(collectionId: string) {\n const file = this.getCollectionFile(collectionId);\n if (file) {\n file.deleteImmediatelySync();\n }\n }\n}\n","import { Hono } from \"hono\";\nimport { AstSchemaEditor } from \"./ast-schema-editor\";\nimport { errorHandler } from \"./errors\";\nimport { HonoEnv } from \"./types\";\n\nexport function createSchemaEditorRoutes(collectionsDir: string): Hono<HonoEnv> {\n const router = new Hono<HonoEnv>();\n router.onError(errorHandler);\n const editor = new AstSchemaEditor(collectionsDir);\n\n router.post(\"/property/save\", async (c) => {\n const body = await c.req.json();\n const { collectionId, propertyKey, propertyConfig } = body;\n await editor.saveProperty(collectionId, propertyKey, propertyConfig);\n return c.json({ success: true });\n });\n\n router.post(\"/property/delete\", async (c) => {\n const body = await c.req.json();\n const { collectionId, propertyKey } = body;\n await editor.deleteProperty(collectionId, propertyKey);\n return c.json({ success: true });\n });\n\n router.post(\"/collection/save\", async (c) => {\n const body = await c.req.json();\n const { collectionId, collectionData } = body;\n await editor.saveCollection(collectionId, collectionData);\n return c.json({ success: true });\n });\n\n router.post(\"/collection/delete\", async (c) => {\n const body = await c.req.json();\n const { collectionId } = body;\n await editor.deleteCollection(collectionId);\n return c.json({ success: true });\n });\n\n return router;\n}\n\n"],"mappings":"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"}