@columnus/shared 0.1.54 → 0.1.56

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 (77) hide show
  1. package/dist/apiProfile.d.ts +39 -0
  2. package/dist/apiProfile.js +19 -2
  3. package/dist/apiProfile.js.map +1 -1
  4. package/dist/assetFamily.d.ts +36 -0
  5. package/dist/assetFamily.js +73 -0
  6. package/dist/assetFamily.js.map +1 -0
  7. package/dist/assetFieldValues.d.ts +21 -0
  8. package/dist/{storageFieldValues.js → assetFieldValues.js} +159 -60
  9. package/dist/assetFieldValues.js.map +1 -0
  10. package/dist/assetGraph.d.ts +199 -0
  11. package/dist/assetGraph.js +214 -0
  12. package/dist/assetGraph.js.map +1 -0
  13. package/dist/assetJobs.d.ts +95 -0
  14. package/dist/assetJobs.js +41 -0
  15. package/dist/assetJobs.js.map +1 -0
  16. package/dist/assetSettings.d.ts +129 -0
  17. package/dist/assetSettings.js +87 -0
  18. package/dist/assetSettings.js.map +1 -0
  19. package/dist/assetValidators.d.ts +475 -0
  20. package/dist/assetValidators.js +83 -0
  21. package/dist/assetValidators.js.map +1 -0
  22. package/dist/bffProtocol.d.ts +29 -24
  23. package/dist/bffProtocol.js +5 -1
  24. package/dist/bffProtocol.js.map +1 -1
  25. package/dist/ciPreviewEnv.js +20 -3
  26. package/dist/ciPreviewEnv.js.map +1 -1
  27. package/dist/clipboardTransfer.js +112 -12
  28. package/dist/clipboardTransfer.js.map +1 -1
  29. package/dist/csv.js +118 -18
  30. package/dist/csv.js.map +1 -1
  31. package/dist/displayLabel.js +1 -1
  32. package/dist/displayLabel.js.map +1 -1
  33. package/dist/dotenv.js +5 -1
  34. package/dist/dotenv.js.map +1 -1
  35. package/dist/importMatch.js +118 -18
  36. package/dist/importMatch.js.map +1 -1
  37. package/dist/index.d.ts +7 -1
  38. package/dist/index.js +741 -82
  39. package/dist/index.js.map +1 -1
  40. package/dist/postLoginPath.d.ts +21 -0
  41. package/dist/postLoginPath.js +24 -0
  42. package/dist/postLoginPath.js.map +1 -0
  43. package/dist/previewDeployName.d.ts +0 -7
  44. package/dist/previewDeployName.js +21 -4
  45. package/dist/previewDeployName.js.map +1 -1
  46. package/dist/recordTableColumns.js +112 -12
  47. package/dist/recordTableColumns.js.map +1 -1
  48. package/dist/recordTablePreload.js.map +1 -1
  49. package/dist/refDisplayRequests.js +112 -12
  50. package/dist/refDisplayRequests.js.map +1 -1
  51. package/dist/richDoc.d.ts +2 -2
  52. package/dist/richDoc.js +5 -5
  53. package/dist/richDoc.js.map +1 -1
  54. package/dist/schemaGenerator.d.ts +25 -1
  55. package/dist/schemaGenerator.js +4569 -18
  56. package/dist/schemaGenerator.js.map +1 -1
  57. package/dist/schemaShrink.d.ts +3 -1
  58. package/dist/schemaShrink.js +4451 -1
  59. package/dist/schemaShrink.js.map +1 -1
  60. package/dist/schemaShrinkValidators.d.ts +2 -2
  61. package/dist/schemaTypes.d.ts +123 -0
  62. package/dist/schemaTypes.js +107 -13
  63. package/dist/schemaTypes.js.map +1 -1
  64. package/dist/share.js +6 -1
  65. package/dist/share.js.map +1 -1
  66. package/dist/shareApiBridge.js +6 -1
  67. package/dist/shareApiBridge.js.map +1 -1
  68. package/dist/shareProposal.js +112 -12
  69. package/dist/shareProposal.js.map +1 -1
  70. package/dist/spaceBundle.js +112 -12
  71. package/dist/spaceBundle.js.map +1 -1
  72. package/dist/syncChannel.d.ts +15 -0
  73. package/dist/syncChannel.js +4642 -0
  74. package/dist/syncChannel.js.map +1 -0
  75. package/package.json +1 -1
  76. package/dist/storageFieldValues.d.ts +0 -10
  77. package/dist/storageFieldValues.js.map +0 -1
package/dist/dotenv.js CHANGED
@@ -22,10 +22,14 @@ function loadEnvFile(filePath) {
22
22
  return {};
23
23
  }
24
24
  }
25
+ function isEnvBlank(key) {
26
+ const current = process.env[key];
27
+ return current === void 0 || current.trim() === "";
28
+ }
25
29
  function applyEnvFile(filePath, opts) {
26
30
  const onlyIfUnset = opts?.onlyIfUnset ?? true;
27
31
  for (const [key, value] of Object.entries(loadEnvFile(filePath))) {
28
- if (!onlyIfUnset || process.env[key] === void 0) {
32
+ if (!onlyIfUnset || isEnvBlank(key)) {
29
33
  process.env[key] = value;
30
34
  }
31
35
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dotenv.ts"],"names":[],"mappings":"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","file":"dotenv.js","sourcesContent":["import { readFileSync, writeFileSync } from \"node:fs\";\n\n/** Parse a dotenv-style file into key/value pairs (no variable expansion). */\nexport function loadEnvFile(filePath: string): Record<string, string> {\n try {\n const raw = readFileSync(filePath, \"utf8\");\n const result: Record<string, string> = {};\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = trimmed.slice(0, eq).trim();\n let value = trimmed.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n result[key] = value;\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/** Merge a dotenv file into `process.env` (default: only keys not already set). */\nexport function applyEnvFile(filePath: string, opts?: { onlyIfUnset?: boolean }): void {\n const onlyIfUnset = opts?.onlyIfUnset ?? true;\n for (const [key, value] of Object.entries(loadEnvFile(filePath))) {\n if (!onlyIfUnset || process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n\n/** Set or append keys in a dotenv file (preserves unrelated lines and comments). */\nexport function upsertEnvFile(filePath: string, updates: Record<string, string>): void {\n let lines: string[] = [];\n try {\n lines = readFileSync(filePath, \"utf8\").split(\"\\n\");\n } catch {\n // new file\n }\n\n const updatedKeys = new Set<string>();\n const out = lines.map((line) => {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) return line;\n const eq = trimmed.indexOf(\"=\");\n if (eq <= 0) return line;\n const key = trimmed.slice(0, eq).trim();\n if (!(key in updates)) return line;\n updatedKeys.add(key);\n return `${key}=${updates[key]}`;\n });\n\n for (const [key, value] of Object.entries(updates)) {\n if (!updatedKeys.has(key)) {\n out.push(`${key}=${value}`);\n }\n }\n\n const body = out.join(\"\\n\").replace(/\\n?$/, \"\\n\");\n writeFileSync(filePath, body, \"utf8\");\n}\n"]}
1
+ {"version":3,"sources":["../src/dotenv.ts"],"names":[],"mappings":"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","file":"dotenv.js","sourcesContent":["import { readFileSync, writeFileSync } from \"node:fs\";\n\n/** Parse a dotenv-style file into key/value pairs (no variable expansion). */\nexport function loadEnvFile(filePath: string): Record<string, string> {\n try {\n const raw = readFileSync(filePath, \"utf8\");\n const result: Record<string, string> = {};\n for (const line of raw.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = trimmed.slice(0, eq).trim();\n let value = trimmed.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n result[key] = value;\n }\n return result;\n } catch {\n return {};\n }\n}\n\n/**\n * True when the process env has no usable value for `key`.\n * Blank strings count as unset: `source`ing an unquoted `CONVEX_DEPLOY_KEY`\n * that contains `|` leaves `KEY=\"\"` in zsh (pipe truncates the assignment),\n * and that must not block loading the real value from the dotenv file.\n */\nfunction isEnvBlank(key: string): boolean {\n const current = process.env[key];\n return current === undefined || current.trim() === \"\";\n}\n\n/** Merge a dotenv file into `process.env` (default: only keys not already set). */\nexport function applyEnvFile(filePath: string, opts?: { onlyIfUnset?: boolean }): void {\n const onlyIfUnset = opts?.onlyIfUnset ?? true;\n for (const [key, value] of Object.entries(loadEnvFile(filePath))) {\n if (!onlyIfUnset || isEnvBlank(key)) {\n process.env[key] = value;\n }\n }\n}\n\n/** Set or append keys in a dotenv file (preserves unrelated lines and comments). */\nexport function upsertEnvFile(filePath: string, updates: Record<string, string>): void {\n let lines: string[] = [];\n try {\n lines = readFileSync(filePath, \"utf8\").split(\"\\n\");\n } catch {\n // new file\n }\n\n const updatedKeys = new Set<string>();\n const out = lines.map((line) => {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) return line;\n const eq = trimmed.indexOf(\"=\");\n if (eq <= 0) return line;\n const key = trimmed.slice(0, eq).trim();\n if (!(key in updates)) return line;\n updatedKeys.add(key);\n return `${key}=${updates[key]}`;\n });\n\n for (const [key, value] of Object.entries(updates)) {\n if (!updatedKeys.has(key)) {\n out.push(`${key}=${value}`);\n }\n }\n\n const body = out.join(\"\\n\").replace(/\\n?$/, \"\\n\");\n writeFileSync(filePath, body, \"utf8\");\n}\n"]}
@@ -4792,7 +4792,7 @@ function extractPlainText(value, maxLength) {
4792
4792
  }
4793
4793
 
4794
4794
  // packages/@columnus/shared/src/displayLabel.ts
4795
- var CONVEX_DOCUMENT_ID = /^[a-z0-9]{32}$/;
4795
+ var CONVEX_DOCUMENT_ID = /^[a-z0-9]{26,64}$/;
4796
4796
  function looksLikeId(s) {
4797
4797
  return CONVEX_DOCUMENT_ID.test(s);
4798
4798
  }
@@ -4822,7 +4822,13 @@ var RESERVED_TABLE_NAMES = /* @__PURE__ */ new Set([
4822
4822
  "columnus_api_output_secrets",
4823
4823
  "columnus_api_sync_routes",
4824
4824
  "columnus_api_outbox",
4825
- "columnus_api_delivery"
4825
+ "columnus_api_delivery",
4826
+ "columnus_assets",
4827
+ "columnus_asset_versions",
4828
+ "columnus_asset_refs",
4829
+ "columnus_asset_profiles",
4830
+ "columnus_asset_settings",
4831
+ "columnus_asset_jobs"
4826
4832
  ]);
4827
4833
  var displayTemplatesSchema = external_exports.object({
4828
4834
  id: external_exports.string().optional(),
@@ -4855,6 +4861,12 @@ var TABLE_COLORS = [
4855
4861
  "lavender"
4856
4862
  ];
4857
4863
  var tableColorSchema = external_exports.enum(TABLE_COLORS);
4864
+ var TABLE_KINDS = ["record", "asset"];
4865
+ var tableKindSchema = external_exports.enum(TABLE_KINDS);
4866
+ var assetBindingSchema = external_exports.object({
4867
+ profile: external_exports.string().min(1),
4868
+ enforced: external_exports.boolean().optional()
4869
+ });
4858
4870
  var fieldTypeSchema = external_exports.lazy(
4859
4871
  () => external_exports.discriminatedUnion("kind", [
4860
4872
  external_exports.object({ kind: external_exports.literal("string") }),
@@ -4882,7 +4894,9 @@ var fieldTypeSchema = external_exports.lazy(
4882
4894
  name: external_exports.string().regex(IDENT),
4883
4895
  label: external_exports.string().optional(),
4884
4896
  type: fieldTypeSchema,
4885
- optional: external_exports.boolean()
4897
+ optional: external_exports.boolean(),
4898
+ /** Nested image/file placements bind a Media profile like top-level ones. */
4899
+ asset: assetBindingSchema.optional()
4886
4900
  })
4887
4901
  )
4888
4902
  }),
@@ -4896,7 +4910,9 @@ var fieldSchema = external_exports.object({
4896
4910
  name: external_exports.string().regex(IDENT, "must be a valid identifier"),
4897
4911
  label: external_exports.string().optional(),
4898
4912
  type: fieldTypeSchema,
4899
- optional: external_exports.boolean()
4913
+ optional: external_exports.boolean(),
4914
+ /** Image/file fields may pin a Media profile; existence is enforced at upload/pick. */
4915
+ asset: assetBindingSchema.optional()
4900
4916
  });
4901
4917
  var indexSchema = external_exports.object({
4902
4918
  name: external_exports.string().regex(IDENT),
@@ -4913,6 +4929,8 @@ var tableSchema = external_exports.object({
4913
4929
  }),
4914
4930
  label: external_exports.string().optional(),
4915
4931
  color: tableColorSchema.optional(),
4932
+ /** Asset metadata tables are typed root tables but never ordinary user records. */
4933
+ kind: tableKindSchema.optional().default("record"),
4916
4934
  fields: external_exports.array(fieldSchema),
4917
4935
  indexes: external_exports.array(indexSchema).default([]),
4918
4936
  searchIndexes: external_exports.array(searchIndexSchema).default([]),
@@ -4922,6 +4940,7 @@ external_exports.object({
4922
4940
  tables: external_exports.array(tableSchema)
4923
4941
  }).superRefine((manifest, ctx) => {
4924
4942
  const tableNames = /* @__PURE__ */ new Set();
4943
+ const tablesByName = /* @__PURE__ */ new Map();
4925
4944
  for (const t of manifest.tables) {
4926
4945
  if (tableNames.has(t.name)) {
4927
4946
  ctx.addIssue({
@@ -4931,6 +4950,37 @@ external_exports.object({
4931
4950
  });
4932
4951
  }
4933
4952
  tableNames.add(t.name);
4953
+ tablesByName.set(t.name, t);
4954
+ if (tableKind(t) === "asset") {
4955
+ if (t.indexes.length > 0) {
4956
+ ctx.addIssue({
4957
+ code: external_exports.ZodIssueCode.custom,
4958
+ message: `${t.name} is an asset metadata table and cannot define indexes`,
4959
+ path: ["tables"]
4960
+ });
4961
+ }
4962
+ if (t.searchIndexes.length > 0) {
4963
+ ctx.addIssue({
4964
+ code: external_exports.ZodIssueCode.custom,
4965
+ message: `${t.name} is an asset metadata table and cannot define search indexes`,
4966
+ path: ["tables"]
4967
+ });
4968
+ }
4969
+ if (t.displayTemplates !== void 0) {
4970
+ ctx.addIssue({
4971
+ code: external_exports.ZodIssueCode.custom,
4972
+ message: `${t.name} is an asset metadata table and cannot define display templates`,
4973
+ path: ["tables"]
4974
+ });
4975
+ }
4976
+ if (t.color !== void 0) {
4977
+ ctx.addIssue({
4978
+ code: external_exports.ZodIssueCode.custom,
4979
+ message: `${t.name} is an asset metadata table and cannot define a color`,
4980
+ path: ["tables"]
4981
+ });
4982
+ }
4983
+ }
4934
4984
  const fieldNames = /* @__PURE__ */ new Set();
4935
4985
  for (const f of t.fields) {
4936
4986
  if (fieldNames.has(f.name)) {
@@ -4941,36 +4991,43 @@ external_exports.object({
4941
4991
  });
4942
4992
  }
4943
4993
  fieldNames.add(f.name);
4994
+ assertAssetBindingAllowed(ctx, t, f);
4944
4995
  }
4945
4996
  }
4946
4997
  for (const t of manifest.tables) {
4947
4998
  for (const f of t.fields) {
4948
- const danglingRef = findDanglingRef(f.type, tableNames);
4949
- if (danglingRef) {
4999
+ const invalidRef = findInvalidReference(f.type, tablesByName);
5000
+ if (invalidRef) {
4950
5001
  ctx.addIssue({
4951
5002
  code: external_exports.ZodIssueCode.custom,
4952
- message: `${t.name}.${f.name} references unknown table: ${danglingRef}`,
5003
+ message: `${t.name}.${f.name} ${invalidRef}`,
4953
5004
  path: ["tables"]
4954
5005
  });
4955
5006
  }
4956
5007
  }
4957
5008
  }
4958
5009
  });
4959
- function findDanglingRef(t, known) {
5010
+ function findInvalidReference(t, tablesByName) {
4960
5011
  switch (t.kind) {
4961
- case "id":
4962
- return known.has(t.table) ? null : t.table;
5012
+ case "id": {
5013
+ const target = tablesByName.get(t.table);
5014
+ if (!target) return `references unknown table: ${t.table}`;
5015
+ if (tableKind(target) === "asset") {
5016
+ return `cannot reference asset metadata table: ${t.table}`;
5017
+ }
5018
+ return null;
5019
+ }
4963
5020
  case "array":
4964
- return findDanglingRef(t.element, known);
5021
+ return findInvalidReference(t.element, tablesByName);
4965
5022
  case "object":
4966
5023
  for (const f of t.fields) {
4967
- const r = findDanglingRef(f.type, known);
5024
+ const r = findInvalidReference(f.type, tablesByName);
4968
5025
  if (r) return r;
4969
5026
  }
4970
5027
  return null;
4971
5028
  case "union":
4972
5029
  for (const o of t.options) {
4973
- const r = findDanglingRef(o, known);
5030
+ const r = findInvalidReference(o, tablesByName);
4974
5031
  if (r) return r;
4975
5032
  }
4976
5033
  return null;
@@ -4978,6 +5035,9 @@ function findDanglingRef(t, known) {
4978
5035
  return null;
4979
5036
  }
4980
5037
  }
5038
+ function tableKind(table) {
5039
+ return table.kind ?? "record";
5040
+ }
4981
5041
  function stripSystemFields(row) {
4982
5042
  const out = {};
4983
5043
  for (const k of Object.keys(row)) {
@@ -4986,6 +5046,46 @@ function stripSystemFields(row) {
4986
5046
  }
4987
5047
  return out;
4988
5048
  }
5049
+ function isFileFieldType(t) {
5050
+ return t.kind === "file" || t.kind === "image";
5051
+ }
5052
+ function isFileArrayFieldType(t) {
5053
+ return t.kind === "array" && (t.element.kind === "file" || t.element.kind === "image");
5054
+ }
5055
+ function isStorageFieldType(t) {
5056
+ return isFileFieldType(t) || isFileArrayFieldType(t);
5057
+ }
5058
+ function assertAssetBindingAllowed(ctx, table, field) {
5059
+ if (field.asset && tableKind(table) === "asset") {
5060
+ ctx.addIssue({
5061
+ code: external_exports.ZodIssueCode.custom,
5062
+ message: `${table.name}.${field.name} asset metadata fields cannot define a Media profile`,
5063
+ path: ["tables"]
5064
+ });
5065
+ } else if (field.asset && !isStorageFieldType(field.type)) {
5066
+ ctx.addIssue({
5067
+ code: external_exports.ZodIssueCode.custom,
5068
+ message: `${table.name}.${field.name} asset profile can only be set on image/file fields`,
5069
+ path: ["tables"]
5070
+ });
5071
+ }
5072
+ assertAssetBindingsInType(ctx, table, field.type);
5073
+ }
5074
+ function assertAssetBindingsInType(ctx, table, type) {
5075
+ switch (type.kind) {
5076
+ case "array":
5077
+ assertAssetBindingsInType(ctx, table, type.element);
5078
+ return;
5079
+ case "object":
5080
+ for (const field of type.fields) assertAssetBindingAllowed(ctx, table, field);
5081
+ return;
5082
+ case "union":
5083
+ for (const option of type.options) assertAssetBindingsInType(ctx, table, option);
5084
+ return;
5085
+ default:
5086
+ return;
5087
+ }
5088
+ }
4989
5089
  function validateFieldValue(value, type, optional) {
4990
5090
  if (value === void 0 || value === null) {
4991
5091
  if (optional) return null;
@@ -5052,10 +5152,10 @@ function validateFieldValue(value, type, optional) {
5052
5152
  }
5053
5153
  }
5054
5154
 
5055
- // packages/@columnus/shared/src/storageFieldValues.ts
5056
- var STORAGE_ID = /^[a-z0-9]+$/i;
5057
- function isValidStorageId(value) {
5058
- return STORAGE_ID.test(value) && value.length >= 10;
5155
+ // packages/@columnus/shared/src/assetFieldValues.ts
5156
+ var ASSET_ID = /^[a-z0-9]+$/i;
5157
+ function isValidAssetId(value) {
5158
+ return ASSET_ID.test(value) && value.length >= 10;
5059
5159
  }
5060
5160
 
5061
5161
  // packages/@columnus/shared/src/csv.ts
@@ -5144,7 +5244,7 @@ function coerceCsvValue(raw, type, optional) {
5144
5244
  return ok(raw);
5145
5245
  case "file":
5146
5246
  case "image":
5147
- if (!isValidStorageId(raw)) return fail("Invalid storage file id");
5247
+ if (!isValidAssetId(raw)) return fail("Invalid asset id");
5148
5248
  return ok(raw);
5149
5249
  case "number": {
5150
5250
  const n = Number(raw);