studiograph 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +18 -0
  2. package/dist/agent/orchestrator.d.ts +69 -0
  3. package/dist/agent/orchestrator.js +211 -0
  4. package/dist/agent/orchestrator.js.map +1 -0
  5. package/dist/agent/tools/graph-tools.d.ts +30 -0
  6. package/dist/agent/tools/graph-tools.js +536 -0
  7. package/dist/agent/tools/graph-tools.js.map +1 -0
  8. package/dist/auth/github.d.ts +53 -0
  9. package/dist/auth/github.js +180 -0
  10. package/dist/auth/github.js.map +1 -0
  11. package/dist/cli/commands/auth.d.ts +10 -0
  12. package/dist/cli/commands/auth.js +63 -0
  13. package/dist/cli/commands/auth.js.map +1 -0
  14. package/dist/cli/commands/init.d.ts +7 -0
  15. package/dist/cli/commands/init.js +299 -0
  16. package/dist/cli/commands/init.js.map +1 -0
  17. package/dist/cli/commands/join.d.ts +14 -0
  18. package/dist/cli/commands/join.js +230 -0
  19. package/dist/cli/commands/join.js.map +1 -0
  20. package/dist/cli/commands/members.d.ts +11 -0
  21. package/dist/cli/commands/members.js +230 -0
  22. package/dist/cli/commands/members.js.map +1 -0
  23. package/dist/cli/commands/serve.d.ts +17 -0
  24. package/dist/cli/commands/serve.js +90 -0
  25. package/dist/cli/commands/serve.js.map +1 -0
  26. package/dist/cli/commands/start.d.ts +7 -0
  27. package/dist/cli/commands/start.js +381 -0
  28. package/dist/cli/commands/start.js.map +1 -0
  29. package/dist/cli/commands/sync.d.ts +10 -0
  30. package/dist/cli/commands/sync.js +121 -0
  31. package/dist/cli/commands/sync.js.map +1 -0
  32. package/dist/cli/index.d.ts +7 -0
  33. package/dist/cli/index.js +31 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/core/graph.d.ts +169 -0
  36. package/dist/core/graph.js +558 -0
  37. package/dist/core/graph.js.map +1 -0
  38. package/dist/core/types.d.ts +216 -0
  39. package/dist/core/types.js +71 -0
  40. package/dist/core/types.js.map +1 -0
  41. package/dist/core/user-config.d.ts +31 -0
  42. package/dist/core/user-config.js +50 -0
  43. package/dist/core/user-config.js.map +1 -0
  44. package/dist/core/validation.d.ts +2371 -0
  45. package/dist/core/validation.js +432 -0
  46. package/dist/core/validation.js.map +1 -0
  47. package/dist/core/workspace-manager.d.ts +104 -0
  48. package/dist/core/workspace-manager.js +432 -0
  49. package/dist/core/workspace-manager.js.map +1 -0
  50. package/dist/core/workspace.d.ts +103 -0
  51. package/dist/core/workspace.js +306 -0
  52. package/dist/core/workspace.js.map +1 -0
  53. package/dist/server/index.d.ts +25 -0
  54. package/dist/server/index.js +84 -0
  55. package/dist/server/index.js.map +1 -0
  56. package/dist/server/plugin-loader.d.ts +31 -0
  57. package/dist/server/plugin-loader.js +81 -0
  58. package/dist/server/plugin-loader.js.map +1 -0
  59. package/dist/server/routes/chat.d.ts +11 -0
  60. package/dist/server/routes/chat.js +66 -0
  61. package/dist/server/routes/chat.js.map +1 -0
  62. package/dist/server/routes/graph-api.d.ts +9 -0
  63. package/dist/server/routes/graph-api.js +72 -0
  64. package/dist/server/routes/graph-api.js.map +1 -0
  65. package/dist/server/routes/webhook.d.ts +14 -0
  66. package/dist/server/routes/webhook.js +69 -0
  67. package/dist/server/routes/webhook.js.map +1 -0
  68. package/dist/services/assets/base.d.ts +69 -0
  69. package/dist/services/assets/base.js +113 -0
  70. package/dist/services/assets/base.js.map +1 -0
  71. package/dist/services/assets/index.d.ts +36 -0
  72. package/dist/services/assets/index.js +89 -0
  73. package/dist/services/assets/index.js.map +1 -0
  74. package/dist/services/assets/local.d.ts +42 -0
  75. package/dist/services/assets/local.js +161 -0
  76. package/dist/services/assets/local.js.map +1 -0
  77. package/dist/services/assets/r2.d.ts +36 -0
  78. package/dist/services/assets/r2.js +182 -0
  79. package/dist/services/assets/r2.js.map +1 -0
  80. package/dist/services/csv-service.d.ts +36 -0
  81. package/dist/services/csv-service.js +143 -0
  82. package/dist/services/csv-service.js.map +1 -0
  83. package/dist/services/git.d.ts +99 -0
  84. package/dist/services/git.js +306 -0
  85. package/dist/services/git.js.map +1 -0
  86. package/dist/services/github-provisioner.d.ts +30 -0
  87. package/dist/services/github-provisioner.js +89 -0
  88. package/dist/services/github-provisioner.js.map +1 -0
  89. package/dist/services/markdown.d.ts +82 -0
  90. package/dist/services/markdown.js +338 -0
  91. package/dist/services/markdown.js.map +1 -0
  92. package/dist/services/memory-service.d.ts +74 -0
  93. package/dist/services/memory-service.js +183 -0
  94. package/dist/services/memory-service.js.map +1 -0
  95. package/dist/utils/git.d.ts +28 -0
  96. package/dist/utils/git.js +55 -0
  97. package/dist/utils/git.js.map +1 -0
  98. package/dist/utils/preflight.d.ts +44 -0
  99. package/dist/utils/preflight.js +95 -0
  100. package/dist/utils/preflight.js.map +1 -0
  101. package/package.json +55 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Local filesystem asset backend
3
+ *
4
+ * Stores assets in .studiograph/assets/ directory
5
+ */
6
+ import { existsSync, mkdirSync, writeFileSync, readdirSync, unlinkSync, statSync } from 'fs';
7
+ import { join, dirname, relative } from 'path';
8
+ import { getMediaType } from './base.js';
9
+ export class LocalAssetBackend {
10
+ basePath;
11
+ constructor(basePath) {
12
+ this.basePath = basePath;
13
+ this.ensureBaseDirectory();
14
+ }
15
+ /**
16
+ * Ensure base directory exists
17
+ */
18
+ ensureBaseDirectory() {
19
+ if (!existsSync(this.basePath)) {
20
+ mkdirSync(this.basePath, { recursive: true });
21
+ }
22
+ }
23
+ /**
24
+ * Get full file path for a key
25
+ */
26
+ getFilePath(key) {
27
+ return join(this.basePath, key);
28
+ }
29
+ /**
30
+ * Upload asset to local filesystem
31
+ */
32
+ async upload(key, buffer, contentType) {
33
+ const filePath = this.getFilePath(key);
34
+ const dir = dirname(filePath);
35
+ // Ensure directory exists
36
+ if (!existsSync(dir)) {
37
+ mkdirSync(dir, { recursive: true });
38
+ }
39
+ // Write file
40
+ writeFileSync(filePath, buffer);
41
+ // Get file size
42
+ const stats = statSync(filePath);
43
+ const filename = key.split('/').pop() || key;
44
+ // Generate relative path for markdown
45
+ // For local files, we use relative paths from workspace root
46
+ const relativePath = relative(process.cwd(), filePath);
47
+ return {
48
+ filename,
49
+ size: stats.size,
50
+ contentType,
51
+ url: `file:///${filePath}`, // Local file URL
52
+ path: relativePath, // Relative path for markdown
53
+ mediaType: getMediaType(contentType),
54
+ };
55
+ }
56
+ /**
57
+ * List assets with a given prefix
58
+ */
59
+ async list(prefix) {
60
+ const dirPath = this.getFilePath(prefix);
61
+ if (!existsSync(dirPath)) {
62
+ return [];
63
+ }
64
+ const assets = [];
65
+ try {
66
+ const files = readdirSync(dirPath);
67
+ for (const file of files) {
68
+ const filePath = join(dirPath, file);
69
+ const stats = statSync(filePath);
70
+ // Skip directories
71
+ if (stats.isDirectory()) {
72
+ continue;
73
+ }
74
+ const key = join(prefix, file);
75
+ const filename = file;
76
+ const relativePath = relative(process.cwd(), filePath);
77
+ // Try to detect content type from filename
78
+ const ext = filename.split('.').pop()?.toLowerCase() || '';
79
+ const contentType = this.guessContentType(ext);
80
+ assets.push({
81
+ filename,
82
+ size: stats.size,
83
+ contentType,
84
+ url: `file:///${filePath}`,
85
+ path: relativePath,
86
+ mediaType: getMediaType(contentType),
87
+ });
88
+ }
89
+ }
90
+ catch (error) {
91
+ // Directory doesn't exist or can't be read
92
+ return [];
93
+ }
94
+ return assets;
95
+ }
96
+ /**
97
+ * Delete a single asset
98
+ */
99
+ async delete(key) {
100
+ const filePath = this.getFilePath(key);
101
+ if (!existsSync(filePath)) {
102
+ throw new Error(`Asset not found: ${key}`);
103
+ }
104
+ unlinkSync(filePath);
105
+ }
106
+ /**
107
+ * Delete all assets with a given prefix
108
+ */
109
+ async deleteAll(prefix) {
110
+ const dirPath = this.getFilePath(prefix);
111
+ if (!existsSync(dirPath)) {
112
+ return 0;
113
+ }
114
+ let count = 0;
115
+ try {
116
+ const files = readdirSync(dirPath);
117
+ for (const file of files) {
118
+ const filePath = join(dirPath, file);
119
+ const stats = statSync(filePath);
120
+ // Only delete files, not directories
121
+ if (!stats.isDirectory()) {
122
+ unlinkSync(filePath);
123
+ count++;
124
+ }
125
+ }
126
+ }
127
+ catch (error) {
128
+ throw new Error(`Failed to delete assets: ${error instanceof Error ? error.message : 'Unknown error'}`);
129
+ }
130
+ return count;
131
+ }
132
+ /**
133
+ * Check if an asset exists
134
+ */
135
+ async exists(key) {
136
+ const filePath = this.getFilePath(key);
137
+ return existsSync(filePath);
138
+ }
139
+ /**
140
+ * Guess content type from file extension
141
+ */
142
+ guessContentType(ext) {
143
+ const contentTypes = {
144
+ png: 'image/png',
145
+ jpg: 'image/jpeg',
146
+ jpeg: 'image/jpeg',
147
+ gif: 'image/gif',
148
+ webp: 'image/webp',
149
+ svg: 'image/svg+xml',
150
+ mp4: 'video/mp4',
151
+ mov: 'video/quicktime',
152
+ webm: 'video/webm',
153
+ mp3: 'audio/mpeg',
154
+ wav: 'audio/wav',
155
+ pdf: 'application/pdf',
156
+ zip: 'application/zip',
157
+ };
158
+ return contentTypes[ext] || 'application/octet-stream';
159
+ }
160
+ }
161
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../../src/services/assets/local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Cloudflare R2 asset backend
3
+ *
4
+ * S3-compatible object storage with zero egress fees
5
+ */
6
+ import type { AssetBackend, AssetInfo, R2Config } from './base.js';
7
+ export declare class R2AssetBackend implements AssetBackend {
8
+ private client;
9
+ private bucket;
10
+ private publicUrl;
11
+ constructor(config: R2Config);
12
+ /**
13
+ * Upload asset to R2
14
+ */
15
+ upload(key: string, buffer: Buffer, contentType: string): Promise<AssetInfo>;
16
+ /**
17
+ * List assets with a given prefix
18
+ */
19
+ list(prefix: string): Promise<AssetInfo[]>;
20
+ /**
21
+ * Delete a single asset
22
+ */
23
+ delete(key: string): Promise<void>;
24
+ /**
25
+ * Delete all assets with a given prefix
26
+ */
27
+ deleteAll(prefix: string): Promise<number>;
28
+ /**
29
+ * Check if an asset exists
30
+ */
31
+ exists(key: string): Promise<boolean>;
32
+ /**
33
+ * Guess content type from file extension
34
+ */
35
+ private guessContentType;
36
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Cloudflare R2 asset backend
3
+ *
4
+ * S3-compatible object storage with zero egress fees
5
+ */
6
+ import { S3Client, PutObjectCommand, ListObjectsV2Command, DeleteObjectCommand, DeleteObjectsCommand, HeadObjectCommand, } from '@aws-sdk/client-s3';
7
+ import { getMediaType } from './base.js';
8
+ export class R2AssetBackend {
9
+ client;
10
+ bucket;
11
+ publicUrl;
12
+ constructor(config) {
13
+ this.bucket = config.bucket;
14
+ this.publicUrl = config.publicUrl.replace(/\/+$/, ''); // Remove trailing slashes
15
+ // Create S3 client configured for R2
16
+ this.client = new S3Client({
17
+ region: 'auto',
18
+ endpoint: `https://${config.accountId}.r2.cloudflarestorage.com`,
19
+ credentials: {
20
+ accessKeyId: config.accessKeyId,
21
+ secretAccessKey: config.secretAccessKey,
22
+ },
23
+ });
24
+ }
25
+ /**
26
+ * Upload asset to R2
27
+ */
28
+ async upload(key, buffer, contentType) {
29
+ try {
30
+ await this.client.send(new PutObjectCommand({
31
+ Bucket: this.bucket,
32
+ Key: key,
33
+ Body: buffer,
34
+ ContentType: contentType,
35
+ CacheControl: 'public, max-age=31536000, immutable', // Cache for 1 year
36
+ }));
37
+ const filename = key.split('/').pop() || key;
38
+ const url = `${this.publicUrl}/${key}`;
39
+ return {
40
+ filename,
41
+ size: buffer.length,
42
+ contentType,
43
+ url,
44
+ path: url, // For R2, use full URL in markdown
45
+ mediaType: getMediaType(contentType),
46
+ };
47
+ }
48
+ catch (error) {
49
+ throw new Error(`Failed to upload to R2: ${error instanceof Error ? error.message : 'Unknown error'}`);
50
+ }
51
+ }
52
+ /**
53
+ * List assets with a given prefix
54
+ */
55
+ async list(prefix) {
56
+ try {
57
+ const response = await this.client.send(new ListObjectsV2Command({
58
+ Bucket: this.bucket,
59
+ Prefix: prefix,
60
+ }));
61
+ const assets = [];
62
+ for (const obj of response.Contents || []) {
63
+ if (!obj.Key)
64
+ continue;
65
+ // Skip if it's a "directory" (ends with /)
66
+ if (obj.Key.endsWith('/'))
67
+ continue;
68
+ const filename = obj.Key.split('/').pop() || obj.Key;
69
+ // Skip if filename contains additional slashes (nested directories)
70
+ const relativePath = obj.Key.replace(prefix, '');
71
+ if (relativePath.includes('/'))
72
+ continue;
73
+ const url = `${this.publicUrl}/${obj.Key}`;
74
+ // Try to get content type from metadata
75
+ let contentType = 'application/octet-stream';
76
+ try {
77
+ const headResponse = await this.client.send(new HeadObjectCommand({
78
+ Bucket: this.bucket,
79
+ Key: obj.Key,
80
+ }));
81
+ contentType = headResponse.ContentType || contentType;
82
+ }
83
+ catch {
84
+ // If HEAD fails, guess from extension
85
+ const ext = filename.split('.').pop()?.toLowerCase() || '';
86
+ contentType = this.guessContentType(ext);
87
+ }
88
+ assets.push({
89
+ filename,
90
+ size: obj.Size || 0,
91
+ contentType,
92
+ url,
93
+ path: url,
94
+ mediaType: getMediaType(contentType),
95
+ });
96
+ }
97
+ return assets;
98
+ }
99
+ catch (error) {
100
+ throw new Error(`Failed to list assets from R2: ${error instanceof Error ? error.message : 'Unknown error'}`);
101
+ }
102
+ }
103
+ /**
104
+ * Delete a single asset
105
+ */
106
+ async delete(key) {
107
+ try {
108
+ await this.client.send(new DeleteObjectCommand({
109
+ Bucket: this.bucket,
110
+ Key: key,
111
+ }));
112
+ }
113
+ catch (error) {
114
+ throw new Error(`Failed to delete from R2: ${error instanceof Error ? error.message : 'Unknown error'}`);
115
+ }
116
+ }
117
+ /**
118
+ * Delete all assets with a given prefix
119
+ */
120
+ async deleteAll(prefix) {
121
+ try {
122
+ // First, list all objects with the prefix
123
+ const response = await this.client.send(new ListObjectsV2Command({
124
+ Bucket: this.bucket,
125
+ Prefix: prefix,
126
+ }));
127
+ const objects = response.Contents || [];
128
+ if (objects.length === 0) {
129
+ return 0;
130
+ }
131
+ // Delete all objects
132
+ await this.client.send(new DeleteObjectsCommand({
133
+ Bucket: this.bucket,
134
+ Delete: {
135
+ Objects: objects.map(obj => ({ Key: obj.Key })),
136
+ Quiet: true,
137
+ },
138
+ }));
139
+ return objects.length;
140
+ }
141
+ catch (error) {
142
+ throw new Error(`Failed to delete assets from R2: ${error instanceof Error ? error.message : 'Unknown error'}`);
143
+ }
144
+ }
145
+ /**
146
+ * Check if an asset exists
147
+ */
148
+ async exists(key) {
149
+ try {
150
+ await this.client.send(new HeadObjectCommand({
151
+ Bucket: this.bucket,
152
+ Key: key,
153
+ }));
154
+ return true;
155
+ }
156
+ catch {
157
+ return false;
158
+ }
159
+ }
160
+ /**
161
+ * Guess content type from file extension
162
+ */
163
+ guessContentType(ext) {
164
+ const contentTypes = {
165
+ png: 'image/png',
166
+ jpg: 'image/jpeg',
167
+ jpeg: 'image/jpeg',
168
+ gif: 'image/gif',
169
+ webp: 'image/webp',
170
+ svg: 'image/svg+xml',
171
+ mp4: 'video/mp4',
172
+ mov: 'video/quicktime',
173
+ webm: 'video/webm',
174
+ mp3: 'audio/mpeg',
175
+ wav: 'audio/wav',
176
+ pdf: 'application/pdf',
177
+ zip: 'application/zip',
178
+ };
179
+ return contentTypes[ext] || 'application/octet-stream';
180
+ }
181
+ }
182
+ //# sourceMappingURL=r2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2.js","sourceRoot":"","sources":["../../../src/services/assets/r2.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * CSV Service
3
+ *
4
+ * Parses, stringifies, validates, and filters CSV data for dataset entities.
5
+ * Uses csv-parse and csv-stringify for reliable CSV handling.
6
+ */
7
+ import type { ColumnDefinition, DatasetRow } from '../core/validation.js';
8
+ export interface DatasetFilter {
9
+ [column: string]: string | number | boolean | {
10
+ gte?: any;
11
+ lte?: any;
12
+ contains?: string;
13
+ };
14
+ }
15
+ export declare class CSVService {
16
+ /**
17
+ * Parse CSV text into typed rows using the declared schema.
18
+ * Returns an empty array for CSV with headers only (no data rows).
19
+ */
20
+ parseRows(csvText: string, schema: ColumnDefinition[]): DatasetRow[];
21
+ /**
22
+ * Stringify rows back to CSV text (header row + data rows).
23
+ */
24
+ stringifyRows(rows: DatasetRow[], schema: ColumnDefinition[]): string;
25
+ /**
26
+ * Validate and coerce a raw string record against the schema.
27
+ * Throws on missing required fields or type coercion failure.
28
+ */
29
+ validateRow(row: Record<string, string>, schema: ColumnDefinition[]): DatasetRow;
30
+ /**
31
+ * Filter rows using a DatasetFilter.
32
+ * Supports equality, { gte, lte } for numbers/dates, { contains } for strings.
33
+ */
34
+ filterRows(rows: DatasetRow[], filter: DatasetFilter): DatasetRow[];
35
+ private coerceValue;
36
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * CSV Service
3
+ *
4
+ * Parses, stringifies, validates, and filters CSV data for dataset entities.
5
+ * Uses csv-parse and csv-stringify for reliable CSV handling.
6
+ */
7
+ import { parse } from 'csv-parse/sync';
8
+ import { stringify } from 'csv-stringify/sync';
9
+ export class CSVService {
10
+ /**
11
+ * Parse CSV text into typed rows using the declared schema.
12
+ * Returns an empty array for CSV with headers only (no data rows).
13
+ */
14
+ parseRows(csvText, schema) {
15
+ const trimmed = csvText.trim();
16
+ if (!trimmed) {
17
+ return [];
18
+ }
19
+ const records = parse(trimmed, {
20
+ columns: true,
21
+ skip_empty_lines: true,
22
+ trim: true,
23
+ });
24
+ return records.map(record => this.validateRow(record, schema));
25
+ }
26
+ /**
27
+ * Stringify rows back to CSV text (header row + data rows).
28
+ */
29
+ stringifyRows(rows, schema) {
30
+ const columns = schema.map(col => col.name);
31
+ if (rows.length === 0) {
32
+ return columns.join(',') + '\n';
33
+ }
34
+ return stringify(rows, {
35
+ header: true,
36
+ columns,
37
+ cast: {
38
+ boolean: (value) => String(value),
39
+ },
40
+ });
41
+ }
42
+ /**
43
+ * Validate and coerce a raw string record against the schema.
44
+ * Throws on missing required fields or type coercion failure.
45
+ */
46
+ validateRow(row, schema) {
47
+ const result = {};
48
+ for (const col of schema) {
49
+ const raw = row[col.name];
50
+ if (raw === undefined || raw === null || raw === '') {
51
+ if (col.required) {
52
+ throw new Error(`Missing required field: ${col.name}`);
53
+ }
54
+ result[col.name] = null;
55
+ continue;
56
+ }
57
+ result[col.name] = this.coerceValue(raw, col);
58
+ }
59
+ return result;
60
+ }
61
+ /**
62
+ * Filter rows using a DatasetFilter.
63
+ * Supports equality, { gte, lte } for numbers/dates, { contains } for strings.
64
+ */
65
+ filterRows(rows, filter) {
66
+ return rows.filter(row => {
67
+ for (const [column, condition] of Object.entries(filter)) {
68
+ const value = row[column];
69
+ if (typeof condition === 'object' && condition !== null) {
70
+ const { gte, lte, contains } = condition;
71
+ if (contains !== undefined) {
72
+ if (typeof value !== 'string' || !value.includes(contains)) {
73
+ return false;
74
+ }
75
+ }
76
+ if (gte !== undefined) {
77
+ if (value === null || value === undefined)
78
+ return false;
79
+ if (typeof value === 'string' && typeof gte === 'string') {
80
+ if (value < gte)
81
+ return false;
82
+ }
83
+ else {
84
+ if (value < gte)
85
+ return false;
86
+ }
87
+ }
88
+ if (lte !== undefined) {
89
+ if (value === null || value === undefined)
90
+ return false;
91
+ if (typeof value === 'string' && typeof lte === 'string') {
92
+ if (value > lte)
93
+ return false;
94
+ }
95
+ else {
96
+ if (value > lte)
97
+ return false;
98
+ }
99
+ }
100
+ }
101
+ else {
102
+ // Equality check
103
+ if (value !== condition) {
104
+ return false;
105
+ }
106
+ }
107
+ }
108
+ return true;
109
+ });
110
+ }
111
+ coerceValue(raw, col) {
112
+ switch (col.type) {
113
+ case 'number': {
114
+ const n = Number(raw);
115
+ if (isNaN(n)) {
116
+ throw new Error(`Invalid number value for column "${col.name}": ${raw}`);
117
+ }
118
+ return n;
119
+ }
120
+ case 'boolean': {
121
+ const lower = raw.toLowerCase();
122
+ if (lower === 'true' || lower === '1' || lower === 'yes')
123
+ return true;
124
+ if (lower === 'false' || lower === '0' || lower === 'no')
125
+ return false;
126
+ throw new Error(`Invalid boolean value for column "${col.name}": ${raw}`);
127
+ }
128
+ case 'date': {
129
+ // Store dates as ISO string; validate that the value is parseable
130
+ const d = new Date(raw);
131
+ if (isNaN(d.getTime())) {
132
+ throw new Error(`Invalid date value for column "${col.name}": ${raw}`);
133
+ }
134
+ // Return the original date string (YYYY-MM-DD) rather than converting to full ISO
135
+ return raw;
136
+ }
137
+ case 'string':
138
+ default:
139
+ return raw;
140
+ }
141
+ }
142
+ }
143
+ //# sourceMappingURL=csv-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-service.js","sourceRoot":"","sources":["../../src/services/csv-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Git service for Studiograph
3
+ *
4
+ * Handles Git operations with transaction-based commits,
5
+ * proper user attribution, and security.
6
+ */
7
+ export interface GitUser {
8
+ id: string;
9
+ name: string;
10
+ email: string;
11
+ }
12
+ export interface GitChange {
13
+ operation: 'create' | 'update' | 'delete';
14
+ filePath: string;
15
+ entityType: string;
16
+ entityId: string;
17
+ }
18
+ export interface GitCommit {
19
+ hash: string;
20
+ author: {
21
+ name: string;
22
+ email: string;
23
+ };
24
+ timestamp: number;
25
+ message: string;
26
+ }
27
+ export declare class GitService {
28
+ private repoPath;
29
+ constructor(repoPath: string);
30
+ /**
31
+ * Ensure the repo path exists and is a directory
32
+ */
33
+ private ensureValidPath;
34
+ /**
35
+ * Initialize Git repository if not already initialized
36
+ */
37
+ private initializeIfNeeded;
38
+ /**
39
+ * Check if repository has any commits
40
+ */
41
+ hasCommits(): boolean;
42
+ /**
43
+ * Get current branch name
44
+ */
45
+ getCurrentBranch(): string;
46
+ /**
47
+ * Create a transaction for batched commits
48
+ */
49
+ createTransaction(): GitTransaction;
50
+ /**
51
+ * Get commit history
52
+ */
53
+ getHistory(limit?: number): GitCommit[];
54
+ /**
55
+ * Get file history for a specific file
56
+ */
57
+ getFileHistory(filePath: string, limit?: number): GitCommit[];
58
+ /**
59
+ * Get current status (modified, added, deleted files)
60
+ */
61
+ getStatus(): {
62
+ modified: string[];
63
+ added: string[];
64
+ deleted: string[];
65
+ untracked: string[];
66
+ };
67
+ /**
68
+ * Check if there are uncommitted changes
69
+ */
70
+ hasUncommittedChanges(): boolean;
71
+ }
72
+ /**
73
+ * Transaction class for batched Git commits
74
+ */
75
+ export declare class GitTransaction {
76
+ private repoPath;
77
+ private changes;
78
+ constructor(repoPath: string);
79
+ /**
80
+ * Add a change to this transaction
81
+ */
82
+ add(change: GitChange): void;
83
+ /**
84
+ * Get all changes in this transaction
85
+ */
86
+ getChanges(): GitChange[];
87
+ /**
88
+ * Commit all changes in this transaction
89
+ */
90
+ commit(user: GitUser, message?: string): Promise<void>;
91
+ /**
92
+ * Generate commit message from changes
93
+ */
94
+ private generateMessage;
95
+ /**
96
+ * Rollback all changes (git reset)
97
+ */
98
+ rollback(): Promise<void>;
99
+ }