@columnus/shared 0.1.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/apiProfile.d.ts +1072 -0
- package/dist/apiProfile.js +4210 -0
- package/dist/apiProfile.js.map +1 -0
- package/dist/apiProfileSlug.d.ts +11 -0
- package/dist/apiProfileSlug.js +39 -0
- package/dist/apiProfileSlug.js.map +1 -0
- package/dist/apiToken.d.ts +6 -0
- package/dist/apiToken.js +35 -0
- package/dist/apiToken.js.map +1 -0
- package/dist/betterAuthSession.d.ts +8 -0
- package/dist/betterAuthSession.js +57 -0
- package/dist/betterAuthSession.js.map +1 -0
- package/dist/bffHost.d.ts +36 -0
- package/dist/bffHost.js +74 -0
- package/dist/bffHost.js.map +1 -0
- package/dist/bffProtocol.d.ts +1357 -0
- package/dist/bffProtocol.js +4362 -0
- package/dist/bffProtocol.js.map +1 -0
- package/dist/bffRedact.d.ts +30 -0
- package/dist/bffRedact.js +55 -0
- package/dist/bffRedact.js.map +1 -0
- package/dist/ciPreviewEnv.d.ts +49 -0
- package/dist/ciPreviewEnv.js +143 -0
- package/dist/ciPreviewEnv.js.map +1 -0
- package/dist/clipboardTransfer.d.ts +25 -0
- package/dist/clipboardTransfer.js +4862 -0
- package/dist/clipboardTransfer.js.map +1 -0
- package/dist/convexSiteUrl.d.ts +73 -0
- package/dist/convexSiteUrl.js +137 -0
- package/dist/convexSiteUrl.js.map +1 -0
- package/dist/csv.d.ts +54 -0
- package/dist/csv.js +5499 -0
- package/dist/csv.js.map +1 -0
- package/dist/dataTransfer.d.ts +21 -0
- package/dist/dataTransfer.js +74 -0
- package/dist/dataTransfer.js.map +1 -0
- package/dist/dateValues.d.ts +18 -0
- package/dist/dateValues.js +73 -0
- package/dist/dateValues.js.map +1 -0
- package/dist/deployOutput.d.ts +3 -0
- package/dist/deployOutput.js +23 -0
- package/dist/deployOutput.js.map +1 -0
- package/dist/displayLabel.d.ts +13 -0
- package/dist/displayLabel.js +4342 -0
- package/dist/displayLabel.js.map +1 -0
- package/dist/displayTemplateGenerator.d.ts +11 -0
- package/dist/displayTemplateGenerator.js +98 -0
- package/dist/displayTemplateGenerator.js.map +1 -0
- package/dist/displayTypes.d.ts +10 -0
- package/dist/displayTypes.js +3 -0
- package/dist/displayTypes.js.map +1 -0
- package/dist/dotenv.d.ts +8 -0
- package/dist/dotenv.js +61 -0
- package/dist/dotenv.js.map +1 -0
- package/dist/erdLayout.d.ts +27 -0
- package/dist/erdLayout.js +78 -0
- package/dist/erdLayout.js.map +1 -0
- package/dist/fieldTypeFormat.d.ts +5 -0
- package/dist/fieldTypeFormat.js +63 -0
- package/dist/fieldTypeFormat.js.map +1 -0
- package/dist/fieldTypeMeta.d.ts +50 -0
- package/dist/fieldTypeMeta.js +84 -0
- package/dist/fieldTypeMeta.js.map +1 -0
- package/dist/fieldValueEqual.d.ts +4 -0
- package/dist/fieldValueEqual.js +58 -0
- package/dist/fieldValueEqual.js.map +1 -0
- package/dist/importMatch.d.ts +80 -0
- package/dist/importMatch.js +5643 -0
- package/dist/importMatch.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +10647 -0
- package/dist/index.js.map +1 -0
- package/dist/inferIdentifier.d.ts +18 -0
- package/dist/inferIdentifier.js +49 -0
- package/dist/inferIdentifier.js.map +1 -0
- package/dist/keyboard.d.ts +16 -0
- package/dist/keyboard.js +71 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/manifestDeploy.d.ts +27 -0
- package/dist/manifestDeploy.js +16 -0
- package/dist/manifestDeploy.js.map +1 -0
- package/dist/manifestHash.d.ts +11 -0
- package/dist/manifestHash.js +35 -0
- package/dist/manifestHash.js.map +1 -0
- package/dist/previewDeployName.d.ts +26 -0
- package/dist/previewDeployName.js +77 -0
- package/dist/previewDeployName.js.map +1 -0
- package/dist/proposePolicy.d.ts +17 -0
- package/dist/proposePolicy.js +33 -0
- package/dist/proposePolicy.js.map +1 -0
- package/dist/queryBetween.d.ts +20 -0
- package/dist/queryBetween.js +28 -0
- package/dist/queryBetween.js.map +1 -0
- package/dist/queryExecution.d.ts +5 -0
- package/dist/queryExecution.js +58 -0
- package/dist/queryExecution.js.map +1 -0
- package/dist/queryFieldOps.d.ts +66 -0
- package/dist/queryFieldOps.js +138 -0
- package/dist/queryFieldOps.js.map +1 -0
- package/dist/queryMerge.d.ts +29 -0
- package/dist/queryMerge.js +4277 -0
- package/dist/queryMerge.js.map +1 -0
- package/dist/queryPlan.d.ts +97 -0
- package/dist/queryPlan.js +4564 -0
- package/dist/queryPlan.js.map +1 -0
- package/dist/queryStudio.d.ts +25 -0
- package/dist/queryStudio.js +4658 -0
- package/dist/queryStudio.js.map +1 -0
- package/dist/queryTypes.d.ts +402 -0
- package/dist/queryTypes.js +4239 -0
- package/dist/queryTypes.js.map +1 -0
- package/dist/queryValidators.d.ts +99 -0
- package/dist/queryValidators.js +76 -0
- package/dist/queryValidators.js.map +1 -0
- package/dist/readApiQuery.d.ts +10 -0
- package/dist/readApiQuery.js +24 -0
- package/dist/readApiQuery.js.map +1 -0
- package/dist/recordTableColumns.d.ts +21 -0
- package/dist/recordTableColumns.js +4469 -0
- package/dist/recordTableColumns.js.map +1 -0
- package/dist/refDisplayRequests.d.ts +17 -0
- package/dist/refDisplayRequests.js +4877 -0
- package/dist/refDisplayRequests.js.map +1 -0
- package/dist/refGraph.d.ts +23 -0
- package/dist/refGraph.js +102 -0
- package/dist/refGraph.js.map +1 -0
- package/dist/refsRecompute.d.ts +8 -0
- package/dist/refsRecompute.js +12 -0
- package/dist/refsRecompute.js.map +1 -0
- package/dist/richDoc.d.ts +67 -0
- package/dist/richDoc.js +4408 -0
- package/dist/richDoc.js.map +1 -0
- package/dist/schemaGenerator.d.ts +31 -0
- package/dist/schemaGenerator.js +198 -0
- package/dist/schemaGenerator.js.map +1 -0
- package/dist/schemaShrink.d.ts +74 -0
- package/dist/schemaShrink.js +132 -0
- package/dist/schemaShrink.js.map +1 -0
- package/dist/schemaShrinkValidators.d.ts +72 -0
- package/dist/schemaShrinkValidators.js +31 -0
- package/dist/schemaShrinkValidators.js.map +1 -0
- package/dist/schemaTypes.d.ts +454 -0
- package/dist/schemaTypes.js +4557 -0
- package/dist/schemaTypes.js.map +1 -0
- package/dist/share.d.ts +8 -0
- package/dist/share.js +4755 -0
- package/dist/share.js.map +1 -0
- package/dist/shareApiBridge.d.ts +18 -0
- package/dist/shareApiBridge.js +4283 -0
- package/dist/shareApiBridge.js.map +1 -0
- package/dist/shareGuestPolicy.d.ts +14 -0
- package/dist/shareGuestPolicy.js +90 -0
- package/dist/shareGuestPolicy.js.map +1 -0
- package/dist/shareProposal.d.ts +113 -0
- package/dist/shareProposal.js +4810 -0
- package/dist/shareProposal.js.map +1 -0
- package/dist/shareProposalDiff.d.ts +13 -0
- package/dist/shareProposalDiff.js +91 -0
- package/dist/shareProposalDiff.js.map +1 -0
- package/dist/shareScopes.d.ts +14 -0
- package/dist/shareScopes.js +223 -0
- package/dist/shareScopes.js.map +1 -0
- package/dist/shareTokenCrypto.d.ts +9 -0
- package/dist/shareTokenCrypto.js +87 -0
- package/dist/shareTokenCrypto.js.map +1 -0
- package/dist/shareTokens.d.ts +6 -0
- package/dist/shareTokens.js +26 -0
- package/dist/shareTokens.js.map +1 -0
- package/dist/shareTypes.d.ts +107 -0
- package/dist/shareTypes.js +3 -0
- package/dist/shareTypes.js.map +1 -0
- package/dist/shareValidators.d.ts +168 -0
- package/dist/shareValidators.js +124 -0
- package/dist/shareValidators.js.map +1 -0
- package/dist/shareWizard.d.ts +14 -0
- package/dist/shareWizard.js +200 -0
- package/dist/shareWizard.js.map +1 -0
- package/dist/spaceBundle.d.ts +67 -0
- package/dist/spaceBundle.js +4664 -0
- package/dist/spaceBundle.js.map +1 -0
- package/dist/storageFieldValues.d.ts +10 -0
- package/dist/storageFieldValues.js +4421 -0
- package/dist/storageFieldValues.js.map +1 -0
- package/dist/studioBoot.d.ts +49 -0
- package/dist/studioBoot.js +167 -0
- package/dist/studioBoot.js.map +1 -0
- package/dist/syncClampMembership.d.ts +22 -0
- package/dist/syncClampMembership.js +74 -0
- package/dist/syncClampMembership.js.map +1 -0
- package/dist/tenantHosts.d.ts +30 -0
- package/dist/tenantHosts.js +60 -0
- package/dist/tenantHosts.js.map +1 -0
- package/dist/tenantSecretsCrypto.d.ts +31 -0
- package/dist/tenantSecretsCrypto.js +145 -0
- package/dist/tenantSecretsCrypto.js.map +1 -0
- package/dist/useDebouncedCommit.d.ts +11 -0
- package/dist/useDebouncedCommit.js +34 -0
- package/dist/useDebouncedCommit.js.map +1 -0
- package/dist/useDebouncedValue.d.ts +1 -0
- package/dist/useDebouncedValue.js +15 -0
- package/dist/useDebouncedValue.js.map +1 -0
- package/dist/useModifierHeld.d.ts +2 -0
- package/dist/useModifierHeld.js +28 -0
- package/dist/useModifierHeld.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Manifest } from "./schemaTypes.ts";
|
|
2
|
+
export type TemplateValidationError = {
|
|
3
|
+
table: string;
|
|
4
|
+
kind: "id" | "preview";
|
|
5
|
+
message: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function validateDisplayTemplates(manifest: Manifest): TemplateValidationError[];
|
|
8
|
+
/** Wrap single-line `return expr;` in String() so generated fns satisfy Promise<string>. */
|
|
9
|
+
export declare function normalizeDisplayTemplateBody(body: string): string;
|
|
10
|
+
export declare function normalizeManifestDisplayTemplates(manifest: Manifest): Manifest;
|
|
11
|
+
export declare function generateDisplayValuesTs(manifest: Manifest): string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// packages/@columnus/shared/src/displayTemplateGenerator.ts
|
|
2
|
+
var BANNER = `// AUTO-GENERATED by Columnus. Do not edit by hand \u2014
|
|
3
|
+
// changes here will be overwritten when Columnus next saves the schema.
|
|
4
|
+
// biome-ignore-all lint/correctness/noUnusedFunctionParameters: lookup is used by template bodies
|
|
5
|
+
`;
|
|
6
|
+
var WRAPPER_PREFIX = 'async (row: Record<string, unknown>, lookup: import("./_lib/displayTypes.js").DisplayLookup): Promise<string> => ';
|
|
7
|
+
function validateDisplayTemplates(manifest) {
|
|
8
|
+
const errors = [];
|
|
9
|
+
for (const table of manifest.tables) {
|
|
10
|
+
const templates = table.displayTemplates;
|
|
11
|
+
if (!templates) continue;
|
|
12
|
+
if (templates.id) {
|
|
13
|
+
const err = validateTemplateBody(templates.id);
|
|
14
|
+
if (err) errors.push({ table: table.name, kind: "id", message: err });
|
|
15
|
+
}
|
|
16
|
+
if (templates.preview) {
|
|
17
|
+
const err = validateTemplateBody(templates.preview);
|
|
18
|
+
if (err) errors.push({ table: table.name, kind: "preview", message: err });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return errors;
|
|
22
|
+
}
|
|
23
|
+
function unwrapTemplateBody(editorValue) {
|
|
24
|
+
const normalized = editorValue.replace(/\r\n/g, "\n").trimEnd();
|
|
25
|
+
const wrappedPatterns = [
|
|
26
|
+
/^\/\/\/ <reference path="[^"]+" \/>\nasync function (?:__template|displayTemplate)\([^)]*\)(?:: Promise<string>)? \{\n?([\s\S]*?)\n\}\n?$/,
|
|
27
|
+
/^async function (?:__template|displayTemplate)\([^)]*\)(?:: Promise<string>)? \{\n?([\s\S]*?)\n\}\n?$/,
|
|
28
|
+
/^async\s*\([^)]*\)\s*=>\s*\{\n?([\s\S]*?)\n\}\n?$/
|
|
29
|
+
];
|
|
30
|
+
for (const pattern of wrappedPatterns) {
|
|
31
|
+
const match = normalized.match(pattern);
|
|
32
|
+
if (match) return (match[1] ?? "").replace(/^\n/, "").replace(/\n$/, "");
|
|
33
|
+
}
|
|
34
|
+
return editorValue;
|
|
35
|
+
}
|
|
36
|
+
function validateTemplateBody(body) {
|
|
37
|
+
const unwrapped = unwrapTemplateBody(body);
|
|
38
|
+
try {
|
|
39
|
+
const AsyncFunction = Object.getPrototypeOf(async () => {
|
|
40
|
+
}).constructor;
|
|
41
|
+
new AsyncFunction("row", "lookup", unwrapped);
|
|
42
|
+
return null;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return err.message;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function normalizeDisplayTemplateBody(body) {
|
|
48
|
+
return body.replace(/\breturn\s+([^;\n]+);/g, (match, expr) => {
|
|
49
|
+
const trimmed = expr.trim();
|
|
50
|
+
if (/^String\s*\(/.test(trimmed)) return match;
|
|
51
|
+
return `return String(${trimmed});`;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function normalizeManifestDisplayTemplates(manifest) {
|
|
55
|
+
return {
|
|
56
|
+
tables: manifest.tables.map((table) => {
|
|
57
|
+
const templates = table.displayTemplates;
|
|
58
|
+
if (!templates) return table;
|
|
59
|
+
return {
|
|
60
|
+
...table,
|
|
61
|
+
displayTemplates: {
|
|
62
|
+
id: templates.id ? normalizeDisplayTemplateBody(templates.id) : void 0,
|
|
63
|
+
preview: templates.preview ? normalizeDisplayTemplateBody(templates.preview) : void 0
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
})
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function renderTemplateFn(body) {
|
|
70
|
+
const unwrapped = unwrapTemplateBody(body);
|
|
71
|
+
return `${WRAPPER_PREFIX}{
|
|
72
|
+
${normalizeDisplayTemplateBody(unwrapped)}
|
|
73
|
+
}`;
|
|
74
|
+
}
|
|
75
|
+
function generateDisplayValuesTs(manifest) {
|
|
76
|
+
const entries = [...manifest.tables].filter((t) => t.displayTemplates?.id || t.displayTemplates?.preview).sort((a, b) => a.name.localeCompare(b.name));
|
|
77
|
+
const tableBlocks = entries.map((t) => {
|
|
78
|
+
const parts = [];
|
|
79
|
+
if (t.displayTemplates?.id) {
|
|
80
|
+
parts.push(` id: ${renderTemplateFn(t.displayTemplates.id)},`);
|
|
81
|
+
}
|
|
82
|
+
if (t.displayTemplates?.preview) {
|
|
83
|
+
parts.push(` preview: ${renderTemplateFn(t.displayTemplates.preview)},`);
|
|
84
|
+
}
|
|
85
|
+
return ` ${JSON.stringify(t.name)}: {
|
|
86
|
+
${parts.join("\n")}
|
|
87
|
+
},`;
|
|
88
|
+
});
|
|
89
|
+
const registry = tableBlocks.length > 0 ? `export const displayTemplates: import("./_lib/displayTypes.js").DisplayTemplateRegistry = {
|
|
90
|
+
${tableBlocks.join("\n")}
|
|
91
|
+
};` : `export const displayTemplates: import("./_lib/displayTypes.js").DisplayTemplateRegistry = {};`;
|
|
92
|
+
return `${BANNER}${registry}
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { generateDisplayValuesTs, normalizeDisplayTemplateBody, normalizeManifestDisplayTemplates, validateDisplayTemplates };
|
|
97
|
+
//# sourceMappingURL=displayTemplateGenerator.js.map
|
|
98
|
+
//# sourceMappingURL=displayTemplateGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/displayTemplateGenerator.ts"],"names":[],"mappings":"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","file":"displayTemplateGenerator.js","sourcesContent":["import type { Manifest } from \"./schemaTypes.ts\";\n\nconst BANNER = `// AUTO-GENERATED by Columnus. Do not edit by hand —\n// changes here will be overwritten when Columnus next saves the schema.\n// biome-ignore-all lint/correctness/noUnusedFunctionParameters: lookup is used by template bodies\n`;\n\nconst WRAPPER_PREFIX =\n 'async (row: Record<string, unknown>, lookup: import(\"./_lib/displayTypes.js\").DisplayLookup): Promise<string> => ';\n\nexport type TemplateValidationError = {\n table: string;\n kind: \"id\" | \"preview\";\n message: string;\n};\n\nexport function validateDisplayTemplates(manifest: Manifest): TemplateValidationError[] {\n const errors: TemplateValidationError[] = [];\n for (const table of manifest.tables) {\n const templates = table.displayTemplates;\n if (!templates) continue;\n if (templates.id) {\n const err = validateTemplateBody(templates.id);\n if (err) errors.push({ table: table.name, kind: \"id\", message: err });\n }\n if (templates.preview) {\n const err = validateTemplateBody(templates.preview);\n if (err) errors.push({ table: table.name, kind: \"preview\", message: err });\n }\n }\n return errors;\n}\n\nfunction unwrapTemplateBody(editorValue: string): string {\n const normalized = editorValue.replace(/\\r\\n/g, \"\\n\").trimEnd();\n\n const wrappedPatterns = [\n /^\\/\\/\\/ <reference path=\"[^\"]+\" \\/>\\nasync function (?:__template|displayTemplate)\\([^)]*\\)(?:: Promise<string>)? \\{\\n?([\\s\\S]*?)\\n\\}\\n?$/,\n /^async function (?:__template|displayTemplate)\\([^)]*\\)(?:: Promise<string>)? \\{\\n?([\\s\\S]*?)\\n\\}\\n?$/,\n /^async\\s*\\([^)]*\\)\\s*=>\\s*\\{\\n?([\\s\\S]*?)\\n\\}\\n?$/,\n ] as const;\n\n for (const pattern of wrappedPatterns) {\n const match = normalized.match(pattern);\n if (match) return (match[1] ?? \"\").replace(/^\\n/, \"\").replace(/\\n$/, \"\");\n }\n\n return editorValue;\n}\n\nfunction validateTemplateBody(body: string): string | null {\n const unwrapped = unwrapTemplateBody(body);\n try {\n // Templates are async function bodies — validate with AsyncFunction so await works.\n const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor as typeof Function;\n // eslint-disable-next-line no-new-func\n new AsyncFunction(\"row\", \"lookup\", unwrapped);\n return null;\n } catch (err) {\n return (err as Error).message;\n }\n}\n\n/** Wrap single-line `return expr;` in String() so generated fns satisfy Promise<string>. */\nexport function normalizeDisplayTemplateBody(body: string): string {\n return body.replace(/\\breturn\\s+([^;\\n]+);/g, (match, expr: string) => {\n const trimmed = expr.trim();\n if (/^String\\s*\\(/.test(trimmed)) return match;\n return `return String(${trimmed});`;\n });\n}\n\nexport function normalizeManifestDisplayTemplates(manifest: Manifest): Manifest {\n return {\n tables: manifest.tables.map((table) => {\n const templates = table.displayTemplates;\n if (!templates) return table;\n return {\n ...table,\n displayTemplates: {\n id: templates.id ? normalizeDisplayTemplateBody(templates.id) : undefined,\n preview: templates.preview ? normalizeDisplayTemplateBody(templates.preview) : undefined,\n },\n };\n }),\n };\n}\n\nfunction renderTemplateFn(body: string): string {\n const unwrapped = unwrapTemplateBody(body);\n return `${WRAPPER_PREFIX}{\\n${normalizeDisplayTemplateBody(unwrapped)}\\n}`;\n}\n\nexport function generateDisplayValuesTs(manifest: Manifest): string {\n const entries = [...manifest.tables]\n .filter((t) => t.displayTemplates?.id || t.displayTemplates?.preview)\n .sort((a, b) => a.name.localeCompare(b.name));\n\n const tableBlocks = entries.map((t) => {\n const parts: string[] = [];\n if (t.displayTemplates?.id) {\n parts.push(` id: ${renderTemplateFn(t.displayTemplates.id)},`);\n }\n if (t.displayTemplates?.preview) {\n parts.push(` preview: ${renderTemplateFn(t.displayTemplates.preview)},`);\n }\n return ` ${JSON.stringify(t.name)}: {\\n${parts.join(\"\\n\")}\\n },`;\n });\n\n const registry =\n tableBlocks.length > 0\n ? `export const displayTemplates: import(\"./_lib/displayTypes.js\").DisplayTemplateRegistry = {\\n${tableBlocks.join(\"\\n\")}\\n};`\n : `export const displayTemplates: import(\"./_lib/displayTypes.js\").DisplayTemplateRegistry = {};`;\n\n return `${BANNER}${registry}\\n`;\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type DisplayLookupResult = {
|
|
2
|
+
id?: string;
|
|
3
|
+
preview?: string;
|
|
4
|
+
};
|
|
5
|
+
export type DisplayLookup = (table: string, id: string) => Promise<DisplayLookupResult | null>;
|
|
6
|
+
export type DisplayTemplateFn = (row: Record<string, unknown>, lookup: DisplayLookup) => Promise<string>;
|
|
7
|
+
export type DisplayTemplateRegistry = Record<string, {
|
|
8
|
+
id?: DisplayTemplateFn;
|
|
9
|
+
preview?: DisplayTemplateFn;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"displayTypes.js"}
|
package/dist/dotenv.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Parse a dotenv-style file into key/value pairs (no variable expansion). */
|
|
2
|
+
export declare function loadEnvFile(filePath: string): Record<string, string>;
|
|
3
|
+
/** Merge a dotenv file into `process.env` (default: only keys not already set). */
|
|
4
|
+
export declare function applyEnvFile(filePath: string, opts?: {
|
|
5
|
+
onlyIfUnset?: boolean;
|
|
6
|
+
}): void;
|
|
7
|
+
/** Set or append keys in a dotenv file (preserves unrelated lines and comments). */
|
|
8
|
+
export declare function upsertEnvFile(filePath: string, updates: Record<string, string>): void;
|
package/dist/dotenv.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
|
|
3
|
+
// packages/@columnus/shared/src/dotenv.ts
|
|
4
|
+
function loadEnvFile(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
const raw = readFileSync(filePath, "utf8");
|
|
7
|
+
const result = {};
|
|
8
|
+
for (const line of raw.split("\n")) {
|
|
9
|
+
const trimmed = line.trim();
|
|
10
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
11
|
+
const eq = trimmed.indexOf("=");
|
|
12
|
+
if (eq <= 0) continue;
|
|
13
|
+
const key = trimmed.slice(0, eq).trim();
|
|
14
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
15
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
16
|
+
value = value.slice(1, -1);
|
|
17
|
+
}
|
|
18
|
+
result[key] = value;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function applyEnvFile(filePath, opts) {
|
|
26
|
+
const onlyIfUnset = opts?.onlyIfUnset ?? true;
|
|
27
|
+
for (const [key, value] of Object.entries(loadEnvFile(filePath))) {
|
|
28
|
+
if (!onlyIfUnset || process.env[key] === void 0) {
|
|
29
|
+
process.env[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function upsertEnvFile(filePath, updates) {
|
|
34
|
+
let lines = [];
|
|
35
|
+
try {
|
|
36
|
+
lines = readFileSync(filePath, "utf8").split("\n");
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
const updatedKeys = /* @__PURE__ */ new Set();
|
|
40
|
+
const out = lines.map((line) => {
|
|
41
|
+
const trimmed = line.trim();
|
|
42
|
+
if (!trimmed || trimmed.startsWith("#")) return line;
|
|
43
|
+
const eq = trimmed.indexOf("=");
|
|
44
|
+
if (eq <= 0) return line;
|
|
45
|
+
const key = trimmed.slice(0, eq).trim();
|
|
46
|
+
if (!(key in updates)) return line;
|
|
47
|
+
updatedKeys.add(key);
|
|
48
|
+
return `${key}=${updates[key]}`;
|
|
49
|
+
});
|
|
50
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
51
|
+
if (!updatedKeys.has(key)) {
|
|
52
|
+
out.push(`${key}=${value}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const body = out.join("\n").replace(/\n?$/, "\n");
|
|
56
|
+
writeFileSync(filePath, body, "utf8");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { applyEnvFile, loadEnvFile, upsertEnvFile };
|
|
60
|
+
//# sourceMappingURL=dotenv.js.map
|
|
61
|
+
//# sourceMappingURL=dotenv.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ErdLayoutDoc = Record<string, {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
}>;
|
|
5
|
+
/** Alias used by the client layer — same shape as ErdLayoutDoc. */
|
|
6
|
+
export type ErdLayout = ErdLayoutDoc;
|
|
7
|
+
/** Strict parse for writes — throws on malformed JSON. */
|
|
8
|
+
export declare function parseErdLayoutOrThrow(layoutJson: string): ErdLayoutDoc;
|
|
9
|
+
/** Best-effort parse for reads/promotion — corrupt stored layout must not block saves. */
|
|
10
|
+
export declare function parseErdLayoutOrEmpty(json: string | undefined): ErdLayoutDoc;
|
|
11
|
+
export declare function serializeErdLayout(layout: ErdLayoutDoc): string;
|
|
12
|
+
export declare function mergeErdLayouts(canonical: ErdLayoutDoc, draft?: ErdLayoutDoc): ErdLayoutDoc;
|
|
13
|
+
export declare function pruneErdLayout(layout: ErdLayoutDoc, tableNames: Iterable<string>): ErdLayoutDoc;
|
|
14
|
+
export declare function mergeDraftErdLayoutIntoCanonical(row: {
|
|
15
|
+
erdLayoutJson?: string;
|
|
16
|
+
draftErdLayoutJson?: string;
|
|
17
|
+
}): {
|
|
18
|
+
erdLayoutJson?: string;
|
|
19
|
+
draftErdLayoutJson: undefined;
|
|
20
|
+
};
|
|
21
|
+
export declare function promoteErdLayoutOnSave(row: {
|
|
22
|
+
erdLayoutJson?: string;
|
|
23
|
+
draftErdLayoutJson?: string;
|
|
24
|
+
} | null, tableNames: Iterable<string>): {
|
|
25
|
+
erdLayoutJson?: string;
|
|
26
|
+
draftErdLayoutJson: undefined;
|
|
27
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// packages/@columnus/shared/src/erdLayout.ts
|
|
2
|
+
function walkLayoutEntries(raw) {
|
|
3
|
+
const out = {};
|
|
4
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
5
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
|
|
6
|
+
const x = value.x;
|
|
7
|
+
const y = value.y;
|
|
8
|
+
if (typeof x === "number" && Number.isFinite(x) && typeof y === "number" && Number.isFinite(y)) {
|
|
9
|
+
out[name] = { x, y };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
function parseErdLayoutOrThrow(layoutJson) {
|
|
15
|
+
let raw;
|
|
16
|
+
try {
|
|
17
|
+
raw = JSON.parse(layoutJson);
|
|
18
|
+
} catch {
|
|
19
|
+
throw new Error("erd layout is not valid JSON");
|
|
20
|
+
}
|
|
21
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
22
|
+
throw new Error("erd layout must be an object");
|
|
23
|
+
}
|
|
24
|
+
return walkLayoutEntries(raw);
|
|
25
|
+
}
|
|
26
|
+
function parseErdLayoutOrEmpty(json) {
|
|
27
|
+
if (!json) return {};
|
|
28
|
+
try {
|
|
29
|
+
return parseErdLayoutOrThrow(json);
|
|
30
|
+
} catch {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function serializeErdLayout(layout) {
|
|
35
|
+
return JSON.stringify(layout);
|
|
36
|
+
}
|
|
37
|
+
function mergeErdLayouts(canonical, draft) {
|
|
38
|
+
if (!draft || Object.keys(draft).length === 0) return { ...canonical };
|
|
39
|
+
return { ...canonical, ...draft };
|
|
40
|
+
}
|
|
41
|
+
function pruneErdLayout(layout, tableNames) {
|
|
42
|
+
const allowed = new Set(tableNames);
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [name, pos] of Object.entries(layout)) {
|
|
45
|
+
if (allowed.has(name)) out[name] = pos;
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function layoutJsonFromDoc(layout) {
|
|
50
|
+
return Object.keys(layout).length > 0 ? serializeErdLayout(layout) : void 0;
|
|
51
|
+
}
|
|
52
|
+
function mergeDraftErdLayoutIntoCanonical(row) {
|
|
53
|
+
if (!row.draftErdLayoutJson) return { draftErdLayoutJson: void 0 };
|
|
54
|
+
const merged = mergeErdLayouts(
|
|
55
|
+
parseErdLayoutOrEmpty(row.erdLayoutJson),
|
|
56
|
+
parseErdLayoutOrEmpty(row.draftErdLayoutJson)
|
|
57
|
+
);
|
|
58
|
+
return {
|
|
59
|
+
erdLayoutJson: layoutJsonFromDoc(merged),
|
|
60
|
+
draftErdLayoutJson: void 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function promoteErdLayoutOnSave(row, tableNames) {
|
|
64
|
+
if (!row) return { draftErdLayoutJson: void 0 };
|
|
65
|
+
const merged = mergeErdLayouts(
|
|
66
|
+
parseErdLayoutOrEmpty(row.erdLayoutJson),
|
|
67
|
+
parseErdLayoutOrEmpty(row.draftErdLayoutJson)
|
|
68
|
+
);
|
|
69
|
+
const pruned = pruneErdLayout(merged, tableNames);
|
|
70
|
+
return {
|
|
71
|
+
erdLayoutJson: layoutJsonFromDoc(pruned),
|
|
72
|
+
draftErdLayoutJson: void 0
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { mergeDraftErdLayoutIntoCanonical, mergeErdLayouts, parseErdLayoutOrEmpty, parseErdLayoutOrThrow, promoteErdLayoutOnSave, pruneErdLayout, serializeErdLayout };
|
|
77
|
+
//# sourceMappingURL=erdLayout.js.map
|
|
78
|
+
//# sourceMappingURL=erdLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/erdLayout.ts"],"names":[],"mappings":"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","file":"erdLayout.js","sourcesContent":["export type ErdLayoutDoc = Record<string, { x: number; y: number }>;\n\n/** Alias used by the client layer — same shape as ErdLayoutDoc. */\nexport type ErdLayout = ErdLayoutDoc;\n\nfunction walkLayoutEntries(raw: Record<string, unknown>): ErdLayoutDoc {\n const out: ErdLayoutDoc = {};\n for (const [name, value] of Object.entries(raw)) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) continue;\n const x = (value as { x?: unknown }).x;\n const y = (value as { y?: unknown }).y;\n if (\n typeof x === \"number\" &&\n Number.isFinite(x) &&\n typeof y === \"number\" &&\n Number.isFinite(y)\n ) {\n out[name] = { x, y };\n }\n }\n return out;\n}\n\n/** Strict parse for writes — throws on malformed JSON. */\nexport function parseErdLayoutOrThrow(layoutJson: string): ErdLayoutDoc {\n let raw: unknown;\n try {\n raw = JSON.parse(layoutJson);\n } catch {\n throw new Error(\"erd layout is not valid JSON\");\n }\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new Error(\"erd layout must be an object\");\n }\n return walkLayoutEntries(raw as Record<string, unknown>);\n}\n\n/** Best-effort parse for reads/promotion — corrupt stored layout must not block saves. */\nexport function parseErdLayoutOrEmpty(json: string | undefined): ErdLayoutDoc {\n if (!json) return {};\n try {\n return parseErdLayoutOrThrow(json);\n } catch {\n return {};\n }\n}\n\nexport function serializeErdLayout(layout: ErdLayoutDoc): string {\n return JSON.stringify(layout);\n}\n\nexport function mergeErdLayouts(canonical: ErdLayoutDoc, draft?: ErdLayoutDoc): ErdLayoutDoc {\n if (!draft || Object.keys(draft).length === 0) return { ...canonical };\n return { ...canonical, ...draft };\n}\n\nexport function pruneErdLayout(layout: ErdLayoutDoc, tableNames: Iterable<string>): ErdLayoutDoc {\n const allowed = new Set(tableNames);\n const out: ErdLayoutDoc = {};\n for (const [name, pos] of Object.entries(layout)) {\n if (allowed.has(name)) out[name] = pos;\n }\n return out;\n}\n\nfunction layoutJsonFromDoc(layout: ErdLayoutDoc): string | undefined {\n return Object.keys(layout).length > 0 ? serializeErdLayout(layout) : undefined;\n}\n\nexport function mergeDraftErdLayoutIntoCanonical(row: {\n erdLayoutJson?: string;\n draftErdLayoutJson?: string;\n}): { erdLayoutJson?: string; draftErdLayoutJson: undefined } {\n if (!row.draftErdLayoutJson) return { draftErdLayoutJson: undefined };\n const merged = mergeErdLayouts(\n parseErdLayoutOrEmpty(row.erdLayoutJson),\n parseErdLayoutOrEmpty(row.draftErdLayoutJson),\n );\n return {\n erdLayoutJson: layoutJsonFromDoc(merged),\n draftErdLayoutJson: undefined,\n };\n}\n\nexport function promoteErdLayoutOnSave(\n row: { erdLayoutJson?: string; draftErdLayoutJson?: string } | null,\n tableNames: Iterable<string>,\n): { erdLayoutJson?: string; draftErdLayoutJson: undefined } {\n if (!row) return { draftErdLayoutJson: undefined };\n const merged = mergeErdLayouts(\n parseErdLayoutOrEmpty(row.erdLayoutJson),\n parseErdLayoutOrEmpty(row.draftErdLayoutJson),\n );\n const pruned = pruneErdLayout(merged, tableNames);\n return {\n erdLayoutJson: layoutJsonFromDoc(pruned),\n draftErdLayoutJson: undefined,\n };\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FieldType } from "./schemaTypes.ts";
|
|
2
|
+
export declare function formatCanonicalFieldType(type: FieldType): string;
|
|
3
|
+
export declare function formatErdCompactFieldType(type: FieldType): string;
|
|
4
|
+
export declare function formatEditorSummaryFieldType(type: FieldType): string;
|
|
5
|
+
export declare function formatMatchLabelFieldType(type: FieldType): string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// packages/@columnus/shared/src/fieldTypeFormat.ts
|
|
2
|
+
function formatCanonicalFieldType(type) {
|
|
3
|
+
switch (type.kind) {
|
|
4
|
+
case "id":
|
|
5
|
+
return `id(${type.table})`;
|
|
6
|
+
case "literal":
|
|
7
|
+
return `"${type.value}"`;
|
|
8
|
+
case "array":
|
|
9
|
+
return `array<${formatCanonicalFieldType(type.element)}>`;
|
|
10
|
+
case "object":
|
|
11
|
+
return "object";
|
|
12
|
+
case "union":
|
|
13
|
+
return type.options.map(formatCanonicalFieldType).join(" | ");
|
|
14
|
+
default:
|
|
15
|
+
return type.kind;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function formatErdCompactFieldType(type) {
|
|
19
|
+
if (type.kind === "union" && type.options.length >= 2 && type.options.every((option) => option.kind === "literal")) {
|
|
20
|
+
return `enum(${type.options.length})`;
|
|
21
|
+
}
|
|
22
|
+
if (type.kind === "union" && type.options.length >= 3) {
|
|
23
|
+
return `union(${type.options.length})`;
|
|
24
|
+
}
|
|
25
|
+
if (type.kind === "array") {
|
|
26
|
+
return `array<${formatErdCompactFieldType(type.element)}>`;
|
|
27
|
+
}
|
|
28
|
+
return formatCanonicalFieldType(type);
|
|
29
|
+
}
|
|
30
|
+
function formatEditorSummaryFieldType(type) {
|
|
31
|
+
switch (type.kind) {
|
|
32
|
+
case "id":
|
|
33
|
+
return `id<${type.table || "?"}>`;
|
|
34
|
+
case "literal":
|
|
35
|
+
return typeof type.value === "string" ? `"${type.value}"` : String(type.value);
|
|
36
|
+
case "array":
|
|
37
|
+
return `${formatEditorSummaryFieldType(type.element)}[]`;
|
|
38
|
+
case "object": {
|
|
39
|
+
const names = type.fields.map((field) => field.name + (field.optional ? "?" : ""));
|
|
40
|
+
if (names.length === 0) return "{}";
|
|
41
|
+
if (names.length <= 3) return `{ ${names.join(", ")} }`;
|
|
42
|
+
return `{ ${names.slice(0, 3).join(", ")}, +${names.length - 3} }`;
|
|
43
|
+
}
|
|
44
|
+
case "union":
|
|
45
|
+
if (type.options.length <= 3) {
|
|
46
|
+
return type.options.map(formatEditorSummaryFieldType).join(" | ");
|
|
47
|
+
}
|
|
48
|
+
return `${type.options.slice(0, 2).map(formatEditorSummaryFieldType).join(" | ")} | +${type.options.length - 2}`;
|
|
49
|
+
default:
|
|
50
|
+
return type.kind;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function formatMatchLabelFieldType(type) {
|
|
54
|
+
if (type.kind === "id") return `id \u2192 ${type.table}`;
|
|
55
|
+
if (type.kind === "array" && type.element.kind === "id") {
|
|
56
|
+
return `id[] \u2192 ${type.element.table}`;
|
|
57
|
+
}
|
|
58
|
+
return type.kind;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { formatCanonicalFieldType, formatEditorSummaryFieldType, formatErdCompactFieldType, formatMatchLabelFieldType };
|
|
62
|
+
//# sourceMappingURL=fieldTypeFormat.js.map
|
|
63
|
+
//# sourceMappingURL=fieldTypeFormat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fieldTypeFormat.ts"],"names":[],"mappings":"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","file":"fieldTypeFormat.js","sourcesContent":["import type { FieldType } from \"./schemaTypes.ts\";\n\nexport function formatCanonicalFieldType(type: FieldType): string {\n switch (type.kind) {\n case \"id\":\n return `id(${type.table})`;\n case \"literal\":\n return `\"${type.value}\"`;\n case \"array\":\n return `array<${formatCanonicalFieldType(type.element)}>`;\n case \"object\":\n return \"object\";\n case \"union\":\n return type.options.map(formatCanonicalFieldType).join(\" | \");\n default:\n return type.kind;\n }\n}\n\nexport function formatErdCompactFieldType(type: FieldType): string {\n if (\n type.kind === \"union\" &&\n type.options.length >= 2 &&\n type.options.every((option) => option.kind === \"literal\")\n ) {\n return `enum(${type.options.length})`;\n }\n if (type.kind === \"union\" && type.options.length >= 3) {\n return `union(${type.options.length})`;\n }\n if (type.kind === \"array\") {\n return `array<${formatErdCompactFieldType(type.element)}>`;\n }\n return formatCanonicalFieldType(type);\n}\n\nexport function formatEditorSummaryFieldType(type: FieldType): string {\n switch (type.kind) {\n case \"id\":\n return `id<${type.table || \"?\"}>`;\n case \"literal\":\n return typeof type.value === \"string\" ? `\"${type.value}\"` : String(type.value);\n case \"array\":\n return `${formatEditorSummaryFieldType(type.element)}[]`;\n case \"object\": {\n const names = type.fields.map((field) => field.name + (field.optional ? \"?\" : \"\"));\n if (names.length === 0) return \"{}\";\n if (names.length <= 3) return `{ ${names.join(\", \")} }`;\n return `{ ${names.slice(0, 3).join(\", \")}, +${names.length - 3} }`;\n }\n case \"union\":\n if (type.options.length <= 3) {\n return type.options.map(formatEditorSummaryFieldType).join(\" | \");\n }\n return `${type.options.slice(0, 2).map(formatEditorSummaryFieldType).join(\" | \")} | +${type.options.length - 2}`;\n default:\n return type.kind;\n }\n}\n\nexport function formatMatchLabelFieldType(type: FieldType): string {\n if (type.kind === \"id\") return `id → ${type.table}`;\n if (type.kind === \"array\" && type.element.kind === \"id\") {\n return `id[] → ${type.element.table}`;\n }\n return type.kind;\n}\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FieldType } from "./schemaTypes.ts";
|
|
2
|
+
/** Manifest field type discriminator — every `FieldType["kind"]`. */
|
|
3
|
+
export type FieldTypeKind = FieldType["kind"];
|
|
4
|
+
/** Kinds offered in schema editors (stable picker order). */
|
|
5
|
+
export declare const FIELD_TYPE_SELECT_KINDS: readonly ["string", "markdown", "number", "boolean", "bigint", "null", "any", "bytes", "file", "image", "date", "datetime", "id", "literal", "array", "object", "union"];
|
|
6
|
+
/** Lucide icon component names for each field kind (resolved in UI). */
|
|
7
|
+
export declare const FIELD_TYPE_KIND_ICONS: {
|
|
8
|
+
readonly string: "Type";
|
|
9
|
+
readonly markdown: "FileText";
|
|
10
|
+
readonly number: "Hash";
|
|
11
|
+
readonly boolean: "ToggleLeft";
|
|
12
|
+
readonly bigint: "Binary";
|
|
13
|
+
readonly null: "CircleSlash";
|
|
14
|
+
readonly any: "Asterisk";
|
|
15
|
+
readonly bytes: "FileCode2";
|
|
16
|
+
readonly file: "File";
|
|
17
|
+
readonly image: "Image";
|
|
18
|
+
readonly date: "Calendar";
|
|
19
|
+
readonly datetime: "CalendarClock";
|
|
20
|
+
readonly id: "Link2";
|
|
21
|
+
readonly literal: "Quote";
|
|
22
|
+
readonly array: "List";
|
|
23
|
+
readonly object: "Braces";
|
|
24
|
+
readonly union: "GitBranch";
|
|
25
|
+
};
|
|
26
|
+
/** Human-readable labels for schema UI (kind ids stay in manifest JSON). */
|
|
27
|
+
export declare const FIELD_TYPE_KIND_LABELS: {
|
|
28
|
+
readonly string: "Text";
|
|
29
|
+
readonly markdown: "Rich text";
|
|
30
|
+
readonly number: "Number";
|
|
31
|
+
readonly boolean: "Boolean";
|
|
32
|
+
readonly bigint: "Big integer";
|
|
33
|
+
readonly null: "Null";
|
|
34
|
+
readonly any: "Any value";
|
|
35
|
+
readonly bytes: "Bytes";
|
|
36
|
+
readonly file: "File";
|
|
37
|
+
readonly image: "Image";
|
|
38
|
+
readonly date: "Date";
|
|
39
|
+
readonly datetime: "Date & time";
|
|
40
|
+
readonly id: "Reference";
|
|
41
|
+
readonly literal: "Literal";
|
|
42
|
+
readonly array: "List";
|
|
43
|
+
readonly object: "Object";
|
|
44
|
+
readonly union: "Union";
|
|
45
|
+
};
|
|
46
|
+
export type FieldTypeKindIconName = (typeof FIELD_TYPE_KIND_ICONS)[FieldTypeKind];
|
|
47
|
+
export declare function fieldTypeKindLabel(kind: FieldTypeKind): string;
|
|
48
|
+
export declare function fieldTypeKindIconName(kind: FieldTypeKind): FieldTypeKindIconName;
|
|
49
|
+
/** Short UI label for a field type (summaries, badges). */
|
|
50
|
+
export declare function briefFieldTypeLabel(type: FieldType): string;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// packages/@columnus/shared/src/fieldTypeMeta.ts
|
|
2
|
+
var FIELD_TYPE_SELECT_KINDS = [
|
|
3
|
+
"string",
|
|
4
|
+
"markdown",
|
|
5
|
+
"number",
|
|
6
|
+
"boolean",
|
|
7
|
+
"bigint",
|
|
8
|
+
"null",
|
|
9
|
+
"any",
|
|
10
|
+
"bytes",
|
|
11
|
+
"file",
|
|
12
|
+
"image",
|
|
13
|
+
"date",
|
|
14
|
+
"datetime",
|
|
15
|
+
"id",
|
|
16
|
+
"literal",
|
|
17
|
+
"array",
|
|
18
|
+
"object",
|
|
19
|
+
"union"
|
|
20
|
+
];
|
|
21
|
+
var FIELD_TYPE_KIND_ICONS = {
|
|
22
|
+
string: "Type",
|
|
23
|
+
markdown: "FileText",
|
|
24
|
+
number: "Hash",
|
|
25
|
+
boolean: "ToggleLeft",
|
|
26
|
+
bigint: "Binary",
|
|
27
|
+
null: "CircleSlash",
|
|
28
|
+
any: "Asterisk",
|
|
29
|
+
bytes: "FileCode2",
|
|
30
|
+
file: "File",
|
|
31
|
+
image: "Image",
|
|
32
|
+
date: "Calendar",
|
|
33
|
+
datetime: "CalendarClock",
|
|
34
|
+
id: "Link2",
|
|
35
|
+
literal: "Quote",
|
|
36
|
+
array: "List",
|
|
37
|
+
object: "Braces",
|
|
38
|
+
union: "GitBranch"
|
|
39
|
+
};
|
|
40
|
+
var FIELD_TYPE_KIND_LABELS = {
|
|
41
|
+
string: "Text",
|
|
42
|
+
markdown: "Rich text",
|
|
43
|
+
number: "Number",
|
|
44
|
+
boolean: "Boolean",
|
|
45
|
+
bigint: "Big integer",
|
|
46
|
+
null: "Null",
|
|
47
|
+
any: "Any value",
|
|
48
|
+
bytes: "Bytes",
|
|
49
|
+
file: "File",
|
|
50
|
+
image: "Image",
|
|
51
|
+
date: "Date",
|
|
52
|
+
datetime: "Date & time",
|
|
53
|
+
id: "Reference",
|
|
54
|
+
literal: "Literal",
|
|
55
|
+
array: "List",
|
|
56
|
+
object: "Object",
|
|
57
|
+
union: "Union"
|
|
58
|
+
};
|
|
59
|
+
function fieldTypeKindLabel(kind) {
|
|
60
|
+
return FIELD_TYPE_KIND_LABELS[kind];
|
|
61
|
+
}
|
|
62
|
+
function fieldTypeKindIconName(kind) {
|
|
63
|
+
return FIELD_TYPE_KIND_ICONS[kind];
|
|
64
|
+
}
|
|
65
|
+
function briefFieldTypeLabel(type) {
|
|
66
|
+
switch (type.kind) {
|
|
67
|
+
case "id":
|
|
68
|
+
return type.table ? `${FIELD_TYPE_KIND_LABELS.id} \u2192 ${type.table}` : FIELD_TYPE_KIND_LABELS.id;
|
|
69
|
+
case "literal":
|
|
70
|
+
return FIELD_TYPE_KIND_LABELS.literal;
|
|
71
|
+
case "array":
|
|
72
|
+
return `${briefFieldTypeLabel(type.element)}[]`;
|
|
73
|
+
case "object":
|
|
74
|
+
return `${FIELD_TYPE_KIND_LABELS.object} (${type.fields.length})`;
|
|
75
|
+
case "union":
|
|
76
|
+
return `${FIELD_TYPE_KIND_LABELS.union} (${type.options.length})`;
|
|
77
|
+
default:
|
|
78
|
+
return fieldTypeKindLabel(type.kind);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { FIELD_TYPE_KIND_ICONS, FIELD_TYPE_KIND_LABELS, FIELD_TYPE_SELECT_KINDS, briefFieldTypeLabel, fieldTypeKindIconName, fieldTypeKindLabel };
|
|
83
|
+
//# sourceMappingURL=fieldTypeMeta.js.map
|
|
84
|
+
//# sourceMappingURL=fieldTypeMeta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fieldTypeMeta.ts"],"names":[],"mappings":";AAMO,IAAM,uBAAA,GAA0B;AAAA,EACrC,QAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,IAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF;AAGO,IAAM,qBAAA,GAAwB;AAAA,EACnC,MAAA,EAAQ,MAAA;AAAA,EACR,QAAA,EAAU,UAAA;AAAA,EACV,MAAA,EAAQ,MAAA;AAAA,EACR,OAAA,EAAS,YAAA;AAAA,EACT,MAAA,EAAQ,QAAA;AAAA,EACR,IAAA,EAAM,aAAA;AAAA,EACN,GAAA,EAAK,UAAA;AAAA,EACL,KAAA,EAAO,WAAA;AAAA,EACP,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,OAAA;AAAA,EACP,IAAA,EAAM,UAAA;AAAA,EACN,QAAA,EAAU,eAAA;AAAA,EACV,EAAA,EAAI,OAAA;AAAA,EACJ,OAAA,EAAS,OAAA;AAAA,EACT,KAAA,EAAO,MAAA;AAAA,EACP,MAAA,EAAQ,QAAA;AAAA,EACR,KAAA,EAAO;AACT;AAGO,IAAM,sBAAA,GAAyB;AAAA,EACpC,MAAA,EAAQ,MAAA;AAAA,EACR,QAAA,EAAU,WAAA;AAAA,EACV,MAAA,EAAQ,QAAA;AAAA,EACR,OAAA,EAAS,SAAA;AAAA,EACT,MAAA,EAAQ,aAAA;AAAA,EACR,IAAA,EAAM,MAAA;AAAA,EACN,GAAA,EAAK,WAAA;AAAA,EACL,KAAA,EAAO,OAAA;AAAA,EACP,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,OAAA;AAAA,EACP,IAAA,EAAM,MAAA;AAAA,EACN,QAAA,EAAU,aAAA;AAAA,EACV,EAAA,EAAI,WAAA;AAAA,EACJ,OAAA,EAAS,SAAA;AAAA,EACT,KAAA,EAAO,MAAA;AAAA,EACP,MAAA,EAAQ,QAAA;AAAA,EACR,KAAA,EAAO;AACT;AAIO,SAAS,mBAAmB,IAAA,EAA6B;AAC9D,EAAA,OAAO,uBAAuB,IAAI,CAAA;AACpC;AAEO,SAAS,sBAAsB,IAAA,EAA4C;AAChF,EAAA,OAAO,sBAAsB,IAAI,CAAA;AACnC;AAGO,SAAS,oBAAoB,IAAA,EAAyB;AAC3D,EAAA,QAAQ,KAAK,IAAA;AAAM,IACjB,KAAK,IAAA;AACH,MAAA,OAAO,IAAA,CAAK,QACR,CAAA,EAAG,sBAAA,CAAuB,EAAE,CAAA,QAAA,EAAM,IAAA,CAAK,KAAK,CAAA,CAAA,GAC5C,sBAAA,CAAuB,EAAA;AAAA,IAC7B,KAAK,SAAA;AACH,MAAA,OAAO,sBAAA,CAAuB,OAAA;AAAA,IAChC,KAAK,OAAA;AACH,MAAA,OAAO,CAAA,EAAG,mBAAA,CAAoB,IAAA,CAAK,OAAO,CAAC,CAAA,EAAA,CAAA;AAAA,IAC7C,KAAK,QAAA;AACH,MAAA,OAAO,GAAG,sBAAA,CAAuB,MAAM,CAAA,EAAA,EAAK,IAAA,CAAK,OAAO,MAAM,CAAA,CAAA,CAAA;AAAA,IAChE,KAAK,OAAA;AACH,MAAA,OAAO,GAAG,sBAAA,CAAuB,KAAK,CAAA,EAAA,EAAK,IAAA,CAAK,QAAQ,MAAM,CAAA,CAAA,CAAA;AAAA,IAChE;AACE,MAAA,OAAO,kBAAA,CAAmB,KAAK,IAAI,CAAA;AAAA;AAEzC","file":"fieldTypeMeta.js","sourcesContent":["import type { FieldType } from \"./schemaTypes.ts\";\n\n/** Manifest field type discriminator — every `FieldType[\"kind\"]`. */\nexport type FieldTypeKind = FieldType[\"kind\"];\n\n/** Kinds offered in schema editors (stable picker order). */\nexport const FIELD_TYPE_SELECT_KINDS = [\n \"string\",\n \"markdown\",\n \"number\",\n \"boolean\",\n \"bigint\",\n \"null\",\n \"any\",\n \"bytes\",\n \"file\",\n \"image\",\n \"date\",\n \"datetime\",\n \"id\",\n \"literal\",\n \"array\",\n \"object\",\n \"union\",\n] as const satisfies readonly FieldTypeKind[];\n\n/** Lucide icon component names for each field kind (resolved in UI). */\nexport const FIELD_TYPE_KIND_ICONS = {\n string: \"Type\",\n markdown: \"FileText\",\n number: \"Hash\",\n boolean: \"ToggleLeft\",\n bigint: \"Binary\",\n null: \"CircleSlash\",\n any: \"Asterisk\",\n bytes: \"FileCode2\",\n file: \"File\",\n image: \"Image\",\n date: \"Calendar\",\n datetime: \"CalendarClock\",\n id: \"Link2\",\n literal: \"Quote\",\n array: \"List\",\n object: \"Braces\",\n union: \"GitBranch\",\n} as const satisfies Record<FieldTypeKind, string>;\n\n/** Human-readable labels for schema UI (kind ids stay in manifest JSON). */\nexport const FIELD_TYPE_KIND_LABELS = {\n string: \"Text\",\n markdown: \"Rich text\",\n number: \"Number\",\n boolean: \"Boolean\",\n bigint: \"Big integer\",\n null: \"Null\",\n any: \"Any value\",\n bytes: \"Bytes\",\n file: \"File\",\n image: \"Image\",\n date: \"Date\",\n datetime: \"Date & time\",\n id: \"Reference\",\n literal: \"Literal\",\n array: \"List\",\n object: \"Object\",\n union: \"Union\",\n} as const satisfies Record<FieldTypeKind, string>;\n\nexport type FieldTypeKindIconName = (typeof FIELD_TYPE_KIND_ICONS)[FieldTypeKind];\n\nexport function fieldTypeKindLabel(kind: FieldTypeKind): string {\n return FIELD_TYPE_KIND_LABELS[kind];\n}\n\nexport function fieldTypeKindIconName(kind: FieldTypeKind): FieldTypeKindIconName {\n return FIELD_TYPE_KIND_ICONS[kind];\n}\n\n/** Short UI label for a field type (summaries, badges). */\nexport function briefFieldTypeLabel(type: FieldType): string {\n switch (type.kind) {\n case \"id\":\n return type.table\n ? `${FIELD_TYPE_KIND_LABELS.id} → ${type.table}`\n : FIELD_TYPE_KIND_LABELS.id;\n case \"literal\":\n return FIELD_TYPE_KIND_LABELS.literal;\n case \"array\":\n return `${briefFieldTypeLabel(type.element)}[]`;\n case \"object\":\n return `${FIELD_TYPE_KIND_LABELS.object} (${type.fields.length})`;\n case \"union\":\n return `${FIELD_TYPE_KIND_LABELS.union} (${type.options.length})`;\n default:\n return fieldTypeKindLabel(type.kind);\n }\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** True when two stored field values are equivalent (missing ≡ undefined). */
|
|
2
|
+
export declare function fieldValuesEqual(a: unknown, b: unknown): boolean;
|
|
3
|
+
/** Drop patch keys whose values match the server row baseline. */
|
|
4
|
+
export declare function filterChangedPatchFields(patch: Record<string, unknown>, baseline: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// packages/@columnus/shared/src/fieldValueEqual.ts
|
|
2
|
+
function bytesToBase64(bytes) {
|
|
3
|
+
let s = "";
|
|
4
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
5
|
+
return btoa(s);
|
|
6
|
+
}
|
|
7
|
+
function jsonReplacer(_key, value) {
|
|
8
|
+
if (typeof value === "bigint") return `${value.toString()}n`;
|
|
9
|
+
if (value instanceof Uint8Array) return { __bytes__: bytesToBase64(value) };
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function normalizeForCompare(value) {
|
|
13
|
+
if (value === void 0 || value === null) return void 0;
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function stableJsonEqual(a, b) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(a, jsonReplacer) === JSON.stringify(b, jsonReplacer);
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function fieldValuesEqual(a, b) {
|
|
24
|
+
const na = normalizeForCompare(a);
|
|
25
|
+
const nb = normalizeForCompare(b);
|
|
26
|
+
if (Object.is(na, nb)) return true;
|
|
27
|
+
if (na === void 0 && nb === void 0) return true;
|
|
28
|
+
const ta = typeof na;
|
|
29
|
+
const tb = typeof nb;
|
|
30
|
+
if (ta !== tb) {
|
|
31
|
+
if (ta === "bigint" && tb === "string") {
|
|
32
|
+
const naBig = na;
|
|
33
|
+
const nbStr = nb;
|
|
34
|
+
return `${naBig.toString()}n` === nbStr || naBig.toString() === nbStr.replace(/n$/, "");
|
|
35
|
+
}
|
|
36
|
+
if (ta === "string" && tb === "bigint") {
|
|
37
|
+
const naStr = na;
|
|
38
|
+
const nbBig = nb;
|
|
39
|
+
return `${nbBig.toString()}n` === naStr || nbBig.toString() === naStr.replace(/n$/, "");
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (ta === "object" && na !== null && nb !== null) {
|
|
44
|
+
return stableJsonEqual(na, nb);
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
function filterChangedPatchFields(patch, baseline) {
|
|
49
|
+
const out = {};
|
|
50
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
51
|
+
if (!fieldValuesEqual(value, baseline?.[key])) out[key] = value;
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { fieldValuesEqual, filterChangedPatchFields };
|
|
57
|
+
//# sourceMappingURL=fieldValueEqual.js.map
|
|
58
|
+
//# sourceMappingURL=fieldValueEqual.js.map
|