hazo_files 1.4.2 → 1.4.3

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.
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  GoogleDriveModule: () => GoogleDriveModule,
47
47
  HAZO_FILES_DEFAULT_TABLE_NAME: () => HAZO_FILES_DEFAULT_TABLE_NAME,
48
48
  HAZO_FILES_MIGRATION_V2: () => HAZO_FILES_MIGRATION_V2,
49
+ HAZO_FILES_MIGRATION_V3: () => HAZO_FILES_MIGRATION_V3,
49
50
  HAZO_FILES_NAMING_DEFAULT_TABLE_NAME: () => HAZO_FILES_NAMING_DEFAULT_TABLE_NAME,
50
51
  HAZO_FILES_NAMING_TABLE_SCHEMA: () => HAZO_FILES_NAMING_TABLE_SCHEMA,
51
52
  HAZO_FILES_TABLE_SCHEMA: () => HAZO_FILES_TABLE_SCHEMA,
@@ -117,6 +118,7 @@ __export(index_exports, {
117
118
  getFileMetadataValues: () => getFileMetadataValues,
118
119
  getMergedData: () => getMergedData,
119
120
  getMigrationForTable: () => getMigrationForTable,
121
+ getMigrationV3ForTable: () => getMigrationV3ForTable,
120
122
  getMimeType: () => getMimeType,
121
123
  getNameWithoutExtension: () => getNameWithoutExtension,
122
124
  getNamingSchemaForTable: () => getNamingSchemaForTable,
@@ -149,6 +151,7 @@ __export(index_exports, {
149
151
  loadConfig: () => loadConfig,
150
152
  loadConfigAsync: () => loadConfigAsync,
151
153
  migrateToV2: () => migrateToV2,
154
+ migrateToV3: () => migrateToV3,
152
155
  normalizePath: () => normalizePath,
153
156
  parseConfig: () => parseConfig,
154
157
  parseFileData: () => parseFileData,
@@ -2397,6 +2400,7 @@ var FileMetadataService = class {
2397
2400
  if (input.scope_id !== void 0) record.scope_id = input.scope_id;
2398
2401
  if (input.uploaded_by !== void 0) record.uploaded_by = input.uploaded_by;
2399
2402
  if (input.original_filename !== void 0) record.original_filename = input.original_filename;
2403
+ if (input.content_tag !== void 0) record.content_tag = input.content_tag;
2400
2404
  const results = await this.crud.insert(record);
2401
2405
  this.logger?.debug?.("Recorded file upload", { path: input.file_path });
2402
2406
  return results[0] || null;
@@ -4231,10 +4235,11 @@ function generatePreviewName(pattern, userVariables, options = {}) {
4231
4235
 
4232
4236
  // src/services/upload-extract-service.ts
4233
4237
  var UploadExtractService = class {
4234
- constructor(fileManager, namingService, extractionService) {
4238
+ constructor(fileManager, namingService, extractionService, defaultContentTagConfig) {
4235
4239
  this.fileManager = fileManager;
4236
4240
  this.namingService = namingService;
4237
4241
  this.extractionService = extractionService;
4242
+ this.defaultContentTagConfig = defaultContentTagConfig;
4238
4243
  }
4239
4244
  /**
4240
4245
  * Upload a file with optional extraction and naming convention
@@ -4311,11 +4316,12 @@ var UploadExtractService = class {
4311
4316
  metadata.extraction_id = extractionData.id;
4312
4317
  metadata.extraction_source = extractionData.source;
4313
4318
  }
4319
+ const effectiveContentTagConfig = options.contentTagConfig ?? this.defaultContentTagConfig;
4320
+ const needsContentTagging = effectiveContentTagConfig?.content_tag_set_by_llm && this.extractionService && this.fileManager.isTrackingActive();
4314
4321
  const uploadResult = await this.fileManager.uploadFile(source, fullPath, {
4315
4322
  ...options,
4316
4323
  metadata,
4317
- awaitRecording: !!extractionData
4318
- // Await recording when extraction needs to be added
4324
+ awaitRecording: !!extractionData || !!needsContentTagging
4319
4325
  });
4320
4326
  if (!uploadResult.success) {
4321
4327
  return {
@@ -4339,13 +4345,23 @@ var UploadExtractService = class {
4339
4345
  );
4340
4346
  }
4341
4347
  }
4348
+ let contentTag;
4349
+ if (needsContentTagging && effectiveContentTagConfig) {
4350
+ contentTag = await this.performContentTagging(
4351
+ source,
4352
+ mimeType,
4353
+ effectiveContentTagConfig,
4354
+ fullPath
4355
+ );
4356
+ }
4342
4357
  return {
4343
4358
  success: true,
4344
4359
  file: uploadResult.data,
4345
4360
  extraction: extractionData,
4346
4361
  generatedPath: fullPath,
4347
4362
  generatedFolderPath: generatedFolderPath || void 0,
4348
- originalFileName
4363
+ originalFileName,
4364
+ contentTag
4349
4365
  };
4350
4366
  } catch (error) {
4351
4367
  const message = error instanceof Error ? error.message : String(error);
@@ -4443,6 +4459,76 @@ var UploadExtractService = class {
4443
4459
  folderPath: folderPath || void 0
4444
4460
  };
4445
4461
  }
4462
+ /**
4463
+ * Perform content tagging via LLM extraction.
4464
+ * Calls the LLM with the configured prompt, extracts the specified field,
4465
+ * and writes it to the content_tag column.
4466
+ */
4467
+ async performContentTagging(buffer, mimeType, config, filePath) {
4468
+ try {
4469
+ if (!this.extractionService) return void 0;
4470
+ const result = await this.extractionService.extract(buffer, mimeType, {
4471
+ promptArea: config.content_tag_prompt_area,
4472
+ promptKey: config.content_tag_prompt_key,
4473
+ promptVariables: config.content_tag_prompt_variables
4474
+ });
4475
+ if (!result.success || !result.data) return void 0;
4476
+ const tagValue = result.data[config.content_tag_prompt_return_fieldname];
4477
+ if (typeof tagValue !== "string" || !tagValue) return void 0;
4478
+ const metadataService = this.fileManager.getMetadataService();
4479
+ if (metadataService) {
4480
+ const storageType = this.fileManager.getProvider() || "local";
4481
+ const record = await metadataService.findByPath(filePath, storageType);
4482
+ if (record) {
4483
+ await metadataService.updateFields(record.id, { content_tag: tagValue });
4484
+ }
4485
+ }
4486
+ return tagValue;
4487
+ } catch {
4488
+ return void 0;
4489
+ }
4490
+ }
4491
+ /**
4492
+ * Manually tag a file's content via LLM.
4493
+ * Works with existing DB records, resolving the file path internally.
4494
+ *
4495
+ * @param fileId - Database record ID of the file
4496
+ * @param config - Content tag config (falls back to default if not provided)
4497
+ * @returns OperationResult with the tag value
4498
+ */
4499
+ async tagFileContent(fileId, config) {
4500
+ const effectiveConfig = config ?? this.defaultContentTagConfig;
4501
+ if (!effectiveConfig || !effectiveConfig.content_tag_set_by_llm) {
4502
+ return { success: false, error: "Content tagging is not configured or disabled" };
4503
+ }
4504
+ if (!this.extractionService) {
4505
+ return { success: false, error: "Extraction service not available" };
4506
+ }
4507
+ const metadataService = this.fileManager.getMetadataService();
4508
+ if (!metadataService) {
4509
+ return { success: false, error: "Metadata service not available (tracking not enabled)" };
4510
+ }
4511
+ const record = await metadataService.findById(fileId);
4512
+ if (!record) {
4513
+ return { success: false, error: `File record not found: ${fileId}` };
4514
+ }
4515
+ const downloadResult = await this.fileManager.downloadFile(record.file_path);
4516
+ if (!downloadResult.success || !downloadResult.data) {
4517
+ return { success: false, error: `Failed to download file: ${downloadResult.error}` };
4518
+ }
4519
+ const buffer = Buffer.isBuffer(downloadResult.data) ? downloadResult.data : Buffer.from(downloadResult.data);
4520
+ const mimeType = getMimeType(record.filename);
4521
+ const tagValue = await this.performContentTagging(
4522
+ buffer,
4523
+ mimeType,
4524
+ effectiveConfig,
4525
+ record.file_path
4526
+ );
4527
+ if (!tagValue) {
4528
+ return { success: false, error: "Content tagging did not produce a result" };
4529
+ }
4530
+ return { success: true, data: tagValue };
4531
+ }
4446
4532
  /**
4447
4533
  * Get the file manager
4448
4534
  */
@@ -4462,8 +4548,8 @@ var UploadExtractService = class {
4462
4548
  return this.extractionService;
4463
4549
  }
4464
4550
  };
4465
- function createUploadExtractService(fileManager, namingService, extractionService) {
4466
- return new UploadExtractService(fileManager, namingService, extractionService);
4551
+ function createUploadExtractService(fileManager, namingService, extractionService, defaultContentTagConfig) {
4552
+ return new UploadExtractService(fileManager, namingService, extractionService, defaultContentTagConfig);
4467
4553
  }
4468
4554
 
4469
4555
  // src/server/factory.ts
@@ -4479,7 +4565,8 @@ async function createHazoFilesServer(options = {}) {
4479
4565
  metadataTableName = "hazo_files",
4480
4566
  namingTableName = "hazo_files_naming",
4481
4567
  enableTracking = !!crudService,
4482
- trackDownloads = true
4568
+ trackDownloads = true,
4569
+ defaultContentTagConfig
4483
4570
  } = options;
4484
4571
  const fileManagerOptions = {
4485
4572
  config,
@@ -4508,7 +4595,8 @@ async function createHazoFilesServer(options = {}) {
4508
4595
  const uploadExtractService = new UploadExtractService(
4509
4596
  fileManager,
4510
4597
  namingService || void 0,
4511
- extractionService || void 0
4598
+ extractionService || void 0,
4599
+ defaultContentTagConfig
4512
4600
  );
4513
4601
  return {
4514
4602
  fileManager,
@@ -4554,7 +4642,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4554
4642
  uploaded_by TEXT,
4555
4643
  storage_verified_at TEXT,
4556
4644
  deleted_at TEXT,
4557
- original_filename TEXT
4645
+ original_filename TEXT,
4646
+ content_tag TEXT
4558
4647
  )`,
4559
4648
  indexes: [
4560
4649
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_path ON hazo_files (file_path)",
@@ -4564,7 +4653,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4564
4653
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_status ON hazo_files (status)",
4565
4654
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_scope ON hazo_files (scope_id)",
4566
4655
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_ref_count ON hazo_files (ref_count)",
4567
- "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)"
4656
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)",
4657
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4568
4658
  ]
4569
4659
  },
4570
4660
  postgres: {
@@ -4587,7 +4677,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4587
4677
  uploaded_by UUID,
4588
4678
  storage_verified_at TIMESTAMP WITH TIME ZONE,
4589
4679
  deleted_at TIMESTAMP WITH TIME ZONE,
4590
- original_filename TEXT
4680
+ original_filename TEXT,
4681
+ content_tag TEXT
4591
4682
  )`,
4592
4683
  indexes: [
4593
4684
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_path ON hazo_files (file_path)",
@@ -4597,7 +4688,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4597
4688
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_status ON hazo_files (status)",
4598
4689
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_scope ON hazo_files (scope_id)",
4599
4690
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_ref_count ON hazo_files (ref_count)",
4600
- "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)"
4691
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)",
4692
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4601
4693
  ]
4602
4694
  },
4603
4695
  columns: [
@@ -4619,7 +4711,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4619
4711
  "uploaded_by",
4620
4712
  "storage_verified_at",
4621
4713
  "deleted_at",
4622
- "original_filename"
4714
+ "original_filename",
4715
+ "content_tag"
4623
4716
  ]
4624
4717
  };
4625
4718
  function getSchemaForTable(tableName, dbType) {
@@ -4760,6 +4853,45 @@ function getNamingSchemaForTable(tableName, dbType) {
4760
4853
  )
4761
4854
  };
4762
4855
  }
4856
+ var HAZO_FILES_MIGRATION_V3 = {
4857
+ tableName: HAZO_FILES_DEFAULT_TABLE_NAME,
4858
+ sqlite: {
4859
+ alterStatements: [
4860
+ "ALTER TABLE hazo_files ADD COLUMN content_tag TEXT"
4861
+ ],
4862
+ indexes: [
4863
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4864
+ ],
4865
+ backfill: ""
4866
+ // No backfill needed — column is nullable, defaults to NULL
4867
+ },
4868
+ postgres: {
4869
+ alterStatements: [
4870
+ "ALTER TABLE hazo_files ADD COLUMN IF NOT EXISTS content_tag TEXT"
4871
+ ],
4872
+ indexes: [
4873
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4874
+ ],
4875
+ backfill: ""
4876
+ // No backfill needed — column is nullable, defaults to NULL
4877
+ },
4878
+ newColumns: [
4879
+ "content_tag"
4880
+ ]
4881
+ };
4882
+ function getMigrationV3ForTable(tableName, dbType) {
4883
+ const migration = HAZO_FILES_MIGRATION_V3[dbType];
4884
+ const defaultName = HAZO_FILES_MIGRATION_V3.tableName;
4885
+ return {
4886
+ alterStatements: migration.alterStatements.map(
4887
+ (stmt) => stmt.replace(new RegExp(defaultName, "g"), tableName)
4888
+ ),
4889
+ indexes: migration.indexes.map(
4890
+ (idx) => idx.replace(new RegExp(defaultName, "g"), tableName)
4891
+ ),
4892
+ backfill: migration.backfill
4893
+ };
4894
+ }
4763
4895
 
4764
4896
  // src/migrations/add-reference-tracking.ts
4765
4897
  async function migrateToV2(executor, dbType, tableName) {
@@ -4779,6 +4911,20 @@ async function backfillV2Defaults(executor, dbType, tableName) {
4779
4911
  await executor.run(migration.backfill);
4780
4912
  }
4781
4913
 
4914
+ // src/migrations/add-content-tag.ts
4915
+ async function migrateToV3(executor, dbType, tableName) {
4916
+ const migration = tableName ? getMigrationV3ForTable(tableName, dbType) : HAZO_FILES_MIGRATION_V3[dbType];
4917
+ for (const stmt of migration.alterStatements) {
4918
+ try {
4919
+ await executor.run(stmt);
4920
+ } catch {
4921
+ }
4922
+ }
4923
+ for (const idx of migration.indexes) {
4924
+ await executor.run(idx);
4925
+ }
4926
+ }
4927
+
4782
4928
  // src/server/index.ts
4783
4929
  try {
4784
4930
  require("server-only");
@@ -4802,6 +4948,7 @@ try {
4802
4948
  GoogleDriveModule,
4803
4949
  HAZO_FILES_DEFAULT_TABLE_NAME,
4804
4950
  HAZO_FILES_MIGRATION_V2,
4951
+ HAZO_FILES_MIGRATION_V3,
4805
4952
  HAZO_FILES_NAMING_DEFAULT_TABLE_NAME,
4806
4953
  HAZO_FILES_NAMING_TABLE_SCHEMA,
4807
4954
  HAZO_FILES_TABLE_SCHEMA,
@@ -4873,6 +5020,7 @@ try {
4873
5020
  getFileMetadataValues,
4874
5021
  getMergedData,
4875
5022
  getMigrationForTable,
5023
+ getMigrationV3ForTable,
4876
5024
  getMimeType,
4877
5025
  getNameWithoutExtension,
4878
5026
  getNamingSchemaForTable,
@@ -4905,6 +5053,7 @@ try {
4905
5053
  loadConfig,
4906
5054
  loadConfigAsync,
4907
5055
  migrateToV2,
5056
+ migrateToV3,
4908
5057
  normalizePath,
4909
5058
  parseConfig,
4910
5059
  parseFileData,
@@ -2226,6 +2226,7 @@ var FileMetadataService = class {
2226
2226
  if (input.scope_id !== void 0) record.scope_id = input.scope_id;
2227
2227
  if (input.uploaded_by !== void 0) record.uploaded_by = input.uploaded_by;
2228
2228
  if (input.original_filename !== void 0) record.original_filename = input.original_filename;
2229
+ if (input.content_tag !== void 0) record.content_tag = input.content_tag;
2229
2230
  const results = await this.crud.insert(record);
2230
2231
  this.logger?.debug?.("Recorded file upload", { path: input.file_path });
2231
2232
  return results[0] || null;
@@ -4060,10 +4061,11 @@ function generatePreviewName(pattern, userVariables, options = {}) {
4060
4061
 
4061
4062
  // src/services/upload-extract-service.ts
4062
4063
  var UploadExtractService = class {
4063
- constructor(fileManager, namingService, extractionService) {
4064
+ constructor(fileManager, namingService, extractionService, defaultContentTagConfig) {
4064
4065
  this.fileManager = fileManager;
4065
4066
  this.namingService = namingService;
4066
4067
  this.extractionService = extractionService;
4068
+ this.defaultContentTagConfig = defaultContentTagConfig;
4067
4069
  }
4068
4070
  /**
4069
4071
  * Upload a file with optional extraction and naming convention
@@ -4140,11 +4142,12 @@ var UploadExtractService = class {
4140
4142
  metadata.extraction_id = extractionData.id;
4141
4143
  metadata.extraction_source = extractionData.source;
4142
4144
  }
4145
+ const effectiveContentTagConfig = options.contentTagConfig ?? this.defaultContentTagConfig;
4146
+ const needsContentTagging = effectiveContentTagConfig?.content_tag_set_by_llm && this.extractionService && this.fileManager.isTrackingActive();
4143
4147
  const uploadResult = await this.fileManager.uploadFile(source, fullPath, {
4144
4148
  ...options,
4145
4149
  metadata,
4146
- awaitRecording: !!extractionData
4147
- // Await recording when extraction needs to be added
4150
+ awaitRecording: !!extractionData || !!needsContentTagging
4148
4151
  });
4149
4152
  if (!uploadResult.success) {
4150
4153
  return {
@@ -4168,13 +4171,23 @@ var UploadExtractService = class {
4168
4171
  );
4169
4172
  }
4170
4173
  }
4174
+ let contentTag;
4175
+ if (needsContentTagging && effectiveContentTagConfig) {
4176
+ contentTag = await this.performContentTagging(
4177
+ source,
4178
+ mimeType,
4179
+ effectiveContentTagConfig,
4180
+ fullPath
4181
+ );
4182
+ }
4171
4183
  return {
4172
4184
  success: true,
4173
4185
  file: uploadResult.data,
4174
4186
  extraction: extractionData,
4175
4187
  generatedPath: fullPath,
4176
4188
  generatedFolderPath: generatedFolderPath || void 0,
4177
- originalFileName
4189
+ originalFileName,
4190
+ contentTag
4178
4191
  };
4179
4192
  } catch (error) {
4180
4193
  const message = error instanceof Error ? error.message : String(error);
@@ -4272,6 +4285,76 @@ var UploadExtractService = class {
4272
4285
  folderPath: folderPath || void 0
4273
4286
  };
4274
4287
  }
4288
+ /**
4289
+ * Perform content tagging via LLM extraction.
4290
+ * Calls the LLM with the configured prompt, extracts the specified field,
4291
+ * and writes it to the content_tag column.
4292
+ */
4293
+ async performContentTagging(buffer, mimeType, config, filePath) {
4294
+ try {
4295
+ if (!this.extractionService) return void 0;
4296
+ const result = await this.extractionService.extract(buffer, mimeType, {
4297
+ promptArea: config.content_tag_prompt_area,
4298
+ promptKey: config.content_tag_prompt_key,
4299
+ promptVariables: config.content_tag_prompt_variables
4300
+ });
4301
+ if (!result.success || !result.data) return void 0;
4302
+ const tagValue = result.data[config.content_tag_prompt_return_fieldname];
4303
+ if (typeof tagValue !== "string" || !tagValue) return void 0;
4304
+ const metadataService = this.fileManager.getMetadataService();
4305
+ if (metadataService) {
4306
+ const storageType = this.fileManager.getProvider() || "local";
4307
+ const record = await metadataService.findByPath(filePath, storageType);
4308
+ if (record) {
4309
+ await metadataService.updateFields(record.id, { content_tag: tagValue });
4310
+ }
4311
+ }
4312
+ return tagValue;
4313
+ } catch {
4314
+ return void 0;
4315
+ }
4316
+ }
4317
+ /**
4318
+ * Manually tag a file's content via LLM.
4319
+ * Works with existing DB records, resolving the file path internally.
4320
+ *
4321
+ * @param fileId - Database record ID of the file
4322
+ * @param config - Content tag config (falls back to default if not provided)
4323
+ * @returns OperationResult with the tag value
4324
+ */
4325
+ async tagFileContent(fileId, config) {
4326
+ const effectiveConfig = config ?? this.defaultContentTagConfig;
4327
+ if (!effectiveConfig || !effectiveConfig.content_tag_set_by_llm) {
4328
+ return { success: false, error: "Content tagging is not configured or disabled" };
4329
+ }
4330
+ if (!this.extractionService) {
4331
+ return { success: false, error: "Extraction service not available" };
4332
+ }
4333
+ const metadataService = this.fileManager.getMetadataService();
4334
+ if (!metadataService) {
4335
+ return { success: false, error: "Metadata service not available (tracking not enabled)" };
4336
+ }
4337
+ const record = await metadataService.findById(fileId);
4338
+ if (!record) {
4339
+ return { success: false, error: `File record not found: ${fileId}` };
4340
+ }
4341
+ const downloadResult = await this.fileManager.downloadFile(record.file_path);
4342
+ if (!downloadResult.success || !downloadResult.data) {
4343
+ return { success: false, error: `Failed to download file: ${downloadResult.error}` };
4344
+ }
4345
+ const buffer = Buffer.isBuffer(downloadResult.data) ? downloadResult.data : Buffer.from(downloadResult.data);
4346
+ const mimeType = getMimeType(record.filename);
4347
+ const tagValue = await this.performContentTagging(
4348
+ buffer,
4349
+ mimeType,
4350
+ effectiveConfig,
4351
+ record.file_path
4352
+ );
4353
+ if (!tagValue) {
4354
+ return { success: false, error: "Content tagging did not produce a result" };
4355
+ }
4356
+ return { success: true, data: tagValue };
4357
+ }
4275
4358
  /**
4276
4359
  * Get the file manager
4277
4360
  */
@@ -4291,8 +4374,8 @@ var UploadExtractService = class {
4291
4374
  return this.extractionService;
4292
4375
  }
4293
4376
  };
4294
- function createUploadExtractService(fileManager, namingService, extractionService) {
4295
- return new UploadExtractService(fileManager, namingService, extractionService);
4377
+ function createUploadExtractService(fileManager, namingService, extractionService, defaultContentTagConfig) {
4378
+ return new UploadExtractService(fileManager, namingService, extractionService, defaultContentTagConfig);
4296
4379
  }
4297
4380
 
4298
4381
  // src/server/factory.ts
@@ -4308,7 +4391,8 @@ async function createHazoFilesServer(options = {}) {
4308
4391
  metadataTableName = "hazo_files",
4309
4392
  namingTableName = "hazo_files_naming",
4310
4393
  enableTracking = !!crudService,
4311
- trackDownloads = true
4394
+ trackDownloads = true,
4395
+ defaultContentTagConfig
4312
4396
  } = options;
4313
4397
  const fileManagerOptions = {
4314
4398
  config,
@@ -4337,7 +4421,8 @@ async function createHazoFilesServer(options = {}) {
4337
4421
  const uploadExtractService = new UploadExtractService(
4338
4422
  fileManager,
4339
4423
  namingService || void 0,
4340
- extractionService || void 0
4424
+ extractionService || void 0,
4425
+ defaultContentTagConfig
4341
4426
  );
4342
4427
  return {
4343
4428
  fileManager,
@@ -4383,7 +4468,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4383
4468
  uploaded_by TEXT,
4384
4469
  storage_verified_at TEXT,
4385
4470
  deleted_at TEXT,
4386
- original_filename TEXT
4471
+ original_filename TEXT,
4472
+ content_tag TEXT
4387
4473
  )`,
4388
4474
  indexes: [
4389
4475
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_path ON hazo_files (file_path)",
@@ -4393,7 +4479,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4393
4479
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_status ON hazo_files (status)",
4394
4480
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_scope ON hazo_files (scope_id)",
4395
4481
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_ref_count ON hazo_files (ref_count)",
4396
- "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)"
4482
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)",
4483
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4397
4484
  ]
4398
4485
  },
4399
4486
  postgres: {
@@ -4416,7 +4503,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4416
4503
  uploaded_by UUID,
4417
4504
  storage_verified_at TIMESTAMP WITH TIME ZONE,
4418
4505
  deleted_at TIMESTAMP WITH TIME ZONE,
4419
- original_filename TEXT
4506
+ original_filename TEXT,
4507
+ content_tag TEXT
4420
4508
  )`,
4421
4509
  indexes: [
4422
4510
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_path ON hazo_files (file_path)",
@@ -4426,7 +4514,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4426
4514
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_status ON hazo_files (status)",
4427
4515
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_scope ON hazo_files (scope_id)",
4428
4516
  "CREATE INDEX IF NOT EXISTS idx_hazo_files_ref_count ON hazo_files (ref_count)",
4429
- "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)"
4517
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_deleted ON hazo_files (deleted_at)",
4518
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4430
4519
  ]
4431
4520
  },
4432
4521
  columns: [
@@ -4448,7 +4537,8 @@ var HAZO_FILES_TABLE_SCHEMA = {
4448
4537
  "uploaded_by",
4449
4538
  "storage_verified_at",
4450
4539
  "deleted_at",
4451
- "original_filename"
4540
+ "original_filename",
4541
+ "content_tag"
4452
4542
  ]
4453
4543
  };
4454
4544
  function getSchemaForTable(tableName, dbType) {
@@ -4589,6 +4679,45 @@ function getNamingSchemaForTable(tableName, dbType) {
4589
4679
  )
4590
4680
  };
4591
4681
  }
4682
+ var HAZO_FILES_MIGRATION_V3 = {
4683
+ tableName: HAZO_FILES_DEFAULT_TABLE_NAME,
4684
+ sqlite: {
4685
+ alterStatements: [
4686
+ "ALTER TABLE hazo_files ADD COLUMN content_tag TEXT"
4687
+ ],
4688
+ indexes: [
4689
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4690
+ ],
4691
+ backfill: ""
4692
+ // No backfill needed — column is nullable, defaults to NULL
4693
+ },
4694
+ postgres: {
4695
+ alterStatements: [
4696
+ "ALTER TABLE hazo_files ADD COLUMN IF NOT EXISTS content_tag TEXT"
4697
+ ],
4698
+ indexes: [
4699
+ "CREATE INDEX IF NOT EXISTS idx_hazo_files_content_tag ON hazo_files (content_tag)"
4700
+ ],
4701
+ backfill: ""
4702
+ // No backfill needed — column is nullable, defaults to NULL
4703
+ },
4704
+ newColumns: [
4705
+ "content_tag"
4706
+ ]
4707
+ };
4708
+ function getMigrationV3ForTable(tableName, dbType) {
4709
+ const migration = HAZO_FILES_MIGRATION_V3[dbType];
4710
+ const defaultName = HAZO_FILES_MIGRATION_V3.tableName;
4711
+ return {
4712
+ alterStatements: migration.alterStatements.map(
4713
+ (stmt) => stmt.replace(new RegExp(defaultName, "g"), tableName)
4714
+ ),
4715
+ indexes: migration.indexes.map(
4716
+ (idx) => idx.replace(new RegExp(defaultName, "g"), tableName)
4717
+ ),
4718
+ backfill: migration.backfill
4719
+ };
4720
+ }
4592
4721
 
4593
4722
  // src/migrations/add-reference-tracking.ts
4594
4723
  async function migrateToV2(executor, dbType, tableName) {
@@ -4608,6 +4737,20 @@ async function backfillV2Defaults(executor, dbType, tableName) {
4608
4737
  await executor.run(migration.backfill);
4609
4738
  }
4610
4739
 
4740
+ // src/migrations/add-content-tag.ts
4741
+ async function migrateToV3(executor, dbType, tableName) {
4742
+ const migration = tableName ? getMigrationV3ForTable(tableName, dbType) : HAZO_FILES_MIGRATION_V3[dbType];
4743
+ for (const stmt of migration.alterStatements) {
4744
+ try {
4745
+ await executor.run(stmt);
4746
+ } catch {
4747
+ }
4748
+ }
4749
+ for (const idx of migration.indexes) {
4750
+ await executor.run(idx);
4751
+ }
4752
+ }
4753
+
4611
4754
  // src/server/index.ts
4612
4755
  try {
4613
4756
  __require("server-only");
@@ -4630,6 +4773,7 @@ export {
4630
4773
  GoogleDriveModule,
4631
4774
  HAZO_FILES_DEFAULT_TABLE_NAME,
4632
4775
  HAZO_FILES_MIGRATION_V2,
4776
+ HAZO_FILES_MIGRATION_V3,
4633
4777
  HAZO_FILES_NAMING_DEFAULT_TABLE_NAME,
4634
4778
  HAZO_FILES_NAMING_TABLE_SCHEMA,
4635
4779
  HAZO_FILES_TABLE_SCHEMA,
@@ -4701,6 +4845,7 @@ export {
4701
4845
  getFileMetadataValues,
4702
4846
  getMergedData,
4703
4847
  getMigrationForTable,
4848
+ getMigrationV3ForTable,
4704
4849
  getMimeType,
4705
4850
  getNameWithoutExtension,
4706
4851
  getNamingSchemaForTable,
@@ -4733,6 +4878,7 @@ export {
4733
4878
  loadConfig,
4734
4879
  loadConfigAsync,
4735
4880
  migrateToV2,
4881
+ migrateToV3,
4736
4882
  normalizePath,
4737
4883
  parseConfig,
4738
4884
  parseFileData,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_files",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "File management including integration to cloud files",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -84,9 +84,8 @@
84
84
  },
85
85
  "peerDependencies": {
86
86
  "@dnd-kit/core": "^6.0.0",
87
- "@dnd-kit/sortable": "^8.0.0",
87
+ "@dnd-kit/sortable": ">=8.0.0",
88
88
  "@dnd-kit/utilities": "^3.0.0",
89
- "hazo_config": ">=2.0.0",
90
89
  "hazo_connect": ">=2.0.0",
91
90
  "hazo_llm_api": ">=1.0.0",
92
91
  "hazo_logs": ">=1.0.0",
@@ -107,9 +106,6 @@
107
106
  "hazo_connect": {
108
107
  "optional": true
109
108
  },
110
- "hazo_config": {
111
- "optional": true
112
- },
113
109
  "hazo_logs": {
114
110
  "optional": true
115
111
  },