@harborclient/team-hub 0.4.7 → 0.4.9

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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.4.8 - 2026-07-28
6
+
7
+ - Enhance environment variable management and UI readiness. (`abab021c`)
8
+ - Update variable schema and enhance environment management. (`34cc08f0`)
9
+
5
10
  ## 0.4.6 - 2026-07-27
6
11
 
7
12
  - Add sidebar marker support across collections, environments, folders, requests, and documents. (`872135d`)
package/dist/cli.js CHANGED
@@ -569,7 +569,8 @@ function mapFirestoreEnvironment(id, data) {
569
569
  createdByUserId: data.createdByUserId ?? null,
570
570
  updatedByUserId: data.updatedByUserId ?? null,
571
571
  deletionLocked: data.deletionLocked ?? false,
572
- marker: readSidebarMarker(data.marker)
572
+ marker: readSidebarMarker(data.marker),
573
+ parentUuid: data.parentUuid?.trim() || null
573
574
  };
574
575
  }
575
576
  function mapFirestoreSnippet(id, data) {
@@ -822,6 +823,10 @@ function normalizeVariable(value) {
822
823
  key: typeof value.key === "string" ? value.key : "",
823
824
  value: typeof value.value === "string" ? value.value : "",
824
825
  defaultValue: typeof value.defaultValue === "string" ? value.defaultValue : "",
826
+ /**
827
+ * Legacy rows without `enabled` remain active so existing data keeps working.
828
+ */
829
+ enabled: value.enabled !== false,
825
830
  share: typeof value.share === "boolean" ? value.share : false
826
831
  };
827
832
  }
@@ -1609,18 +1614,21 @@ var FirestoreDatabase = class _FirestoreDatabase {
1609
1614
  updatedAt: now,
1610
1615
  createdByUserId: actingUserId,
1611
1616
  updatedByUserId: actingUserId,
1612
- deletionLocked: false
1617
+ deletionLocked: false,
1618
+ parentUuid: null
1613
1619
  };
1614
1620
  await this.requireClient().collection(ENVIRONMENTS_COLLECTION).doc(id).set(data);
1615
1621
  await this.recordAuditEntry(actingUserId, "create", "environment", id);
1616
1622
  return mapFirestoreEnvironment(id, data);
1617
1623
  }
1618
1624
  /**
1619
- * Updates an environment's name and variables.
1625
+ * Updates an environment's name, variables, and optional parent link.
1620
1626
  *
1621
1627
  * @param actingUserId - User performing the update action.
1628
+ * @param marker - Optional sidebar marker; omit to leave unchanged.
1629
+ * @param parentUuid - Parent environment id; `null` clears; omit to leave unchanged.
1622
1630
  */
1623
- async updateEnvironment(id, name, variables, actingUserId, marker) {
1631
+ async updateEnvironment(id, name, variables, actingUserId, marker, parentUuid) {
1624
1632
  const trimmedName = trimRequiredName(name, "Environment name");
1625
1633
  const updatedAt = /* @__PURE__ */ new Date();
1626
1634
  const docRef = this.requireClient().collection(ENVIRONMENTS_COLLECTION).doc(id);
@@ -1630,33 +1638,43 @@ var FirestoreDatabase = class _FirestoreDatabase {
1630
1638
  }
1631
1639
  const existing = snapshot.data();
1632
1640
  const serializedMarker = marker !== void 0 ? serializeSidebarMarker(marker) : existing.marker;
1641
+ const normalizedParent = parentUuid === void 0 ? void 0 : parentUuid?.trim() || null;
1633
1642
  const updated = {
1634
1643
  ...existing,
1635
1644
  name: trimmedName,
1636
1645
  variables,
1637
1646
  updatedAt,
1638
1647
  updatedByUserId: actingUserId,
1639
- ...marker !== void 0 ? { marker: serializedMarker } : {}
1648
+ ...marker !== void 0 ? { marker: serializedMarker } : {},
1649
+ ...normalizedParent === void 0 ? {} : { parentUuid: normalizedParent }
1640
1650
  };
1641
1651
  await docRef.update({
1642
1652
  name: trimmedName,
1643
1653
  variables,
1644
1654
  updatedAt,
1645
1655
  updatedByUserId: actingUserId,
1646
- ...marker !== void 0 ? { marker: serializedMarker } : {}
1656
+ ...marker !== void 0 ? { marker: serializedMarker } : {},
1657
+ ...normalizedParent === void 0 ? {} : { parentUuid: normalizedParent }
1647
1658
  });
1648
1659
  await this.recordAuditEntry(actingUserId, "update", "environment", id);
1649
1660
  return mapFirestoreEnvironment(id, updated);
1650
1661
  }
1651
1662
  /**
1652
- * Deletes an environment.
1663
+ * Deletes an environment and orphans any direct children (clears their parentUuid).
1653
1664
  *
1654
1665
  * @param id - Environment ID to delete.
1655
1666
  * @param actingUserId - User performing the delete action.
1656
1667
  */
1657
1668
  async deleteEnvironment(id, actingUserId) {
1658
1669
  await this.recordAuditEntry(actingUserId, "delete", "environment", id);
1659
- await this.requireClient().collection(ENVIRONMENTS_COLLECTION).doc(id).delete();
1670
+ const client = this.requireClient();
1671
+ const children = await client.collection(ENVIRONMENTS_COLLECTION).where("parentUuid", "==", id).get();
1672
+ const batch = client.batch();
1673
+ for (const child of children.docs) {
1674
+ batch.update(child.ref, { parentUuid: null });
1675
+ }
1676
+ batch.delete(client.collection(ENVIRONMENTS_COLLECTION).doc(id));
1677
+ await batch.commit();
1660
1678
  }
1661
1679
  /**
1662
1680
  * Finds an environment by stable identifier.
@@ -2861,7 +2879,8 @@ function mapEnvironmentSqlRow(row) {
2861
2879
  createdByUserId: row.created_by_user_id ?? null,
2862
2880
  updatedByUserId: row.updated_by_user_id ?? null,
2863
2881
  deletionLocked: Boolean(row.deletion_locked),
2864
- marker: readSidebarMarker(row.marker)
2882
+ marker: readSidebarMarker(row.marker),
2883
+ parentUuid: row.parent_uuid?.trim() || null
2865
2884
  };
2866
2885
  }
2867
2886
  function parseSnippetScope(value) {
@@ -2999,8 +3018,10 @@ CREATE TABLE IF NOT EXISTS environments (
2999
3018
  updated_at DATETIME NOT NULL,
3000
3019
  created_by_user_id VARCHAR(36) NULL,
3001
3020
  updated_by_user_id VARCHAR(36) NULL,
3021
+ parent_uuid VARCHAR(36) NULL,
3002
3022
  FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL,
3003
- FOREIGN KEY (updated_by_user_id) REFERENCES users(id) ON DELETE SET NULL
3023
+ FOREIGN KEY (updated_by_user_id) REFERENCES users(id) ON DELETE SET NULL,
3024
+ FOREIGN KEY (parent_uuid) REFERENCES environments(id) ON DELETE SET NULL
3004
3025
  )
3005
3026
  `.trim();
3006
3027
  var SNIPPETS_MIGRATION_SQL = `
@@ -3140,6 +3161,10 @@ var FOLDERS_PARENT_MIGRATION_SQL = `
3140
3161
  ALTER TABLE folders
3141
3162
  ADD COLUMN IF NOT EXISTS parent_folder_id VARCHAR(36) NULL
3142
3163
  `.trim();
3164
+ var ENVIRONMENTS_PARENT_UUID_MIGRATION_SQL = `
3165
+ ALTER TABLE environments
3166
+ ADD COLUMN IF NOT EXISTS parent_uuid VARCHAR(36) NULL
3167
+ `.trim();
3143
3168
  var REQUESTS_ATTRIBUTION_MIGRATION_SQL = `
3144
3169
  ALTER TABLE requests
3145
3170
  ADD COLUMN IF NOT EXISTS created_by_user_id VARCHAR(36) NULL,
@@ -3295,7 +3320,8 @@ var MYSQL_MIGRATIONS = [
3295
3320
  USERS_SNIPPET_ACCESS_BACKFILL_SQL,
3296
3321
  RUN_RESULTS_MIGRATION_SQL,
3297
3322
  USER_INVITATIONS_MIGRATION_SQL,
3298
- ...MARKER_MIGRATIONS_SQL
3323
+ ...MARKER_MIGRATIONS_SQL,
3324
+ ENVIRONMENTS_PARENT_UUID_MIGRATION_SQL
3299
3325
  ];
3300
3326
 
3301
3327
  // src/db/mysql/schemas.ts
@@ -3354,7 +3380,7 @@ function serializeAccessList(access) {
3354
3380
  }
3355
3381
  var USER_SELECT_COLUMNS = `id, name, role, collection_access, environment_access, snippet_access, llm_access, llm_models, llm_monthly_token_limit, created_at, updated_at, created_by_user_id, updated_by_user_id`;
3356
3382
  var COLLECTION_SELECT_COLUMNS = `id, name, variables, headers, auth, pre_request_script, post_request_script, created_at, updated_at, created_by_user_id, updated_by_user_id, deletion_locked, marker`;
3357
- var ENVIRONMENT_SELECT_COLUMNS = `id, name, variables, created_at, updated_at, created_by_user_id, updated_by_user_id, deletion_locked, marker`;
3383
+ var ENVIRONMENT_SELECT_COLUMNS = `id, name, variables, created_at, updated_at, created_by_user_id, updated_by_user_id, deletion_locked, marker, parent_uuid`;
3358
3384
  var SNIPPET_SELECT_COLUMNS = `id, name, code, scope, sort_order, created_at, updated_at, created_by_user_id, updated_by_user_id, deletion_locked`;
3359
3385
  var FOLDER_SELECT_COLUMNS = `id, collection_id, parent_folder_id, name, sort_order, created_at, updated_at, created_by_user_id, updated_by_user_id, marker`;
3360
3386
  var REQUEST_SELECT_COLUMNS = `id, collection_id, folder_id, name, method, url, headers, params, auth, body, body_type, pre_request_script, post_request_script, comment, sort_order, created_at, updated_at, created_by_user_id, updated_by_user_id, marker`;
@@ -4359,37 +4385,36 @@ var MysqlDatabase = class _MysqlDatabase {
4359
4385
  return mapEnvironmentSqlRow(row);
4360
4386
  }
4361
4387
  /**
4362
- * Updates an environment's name and variables.
4388
+ * Updates an environment's name, variables, and optional parent link.
4363
4389
  *
4364
4390
  * @param actingUserId - User performing the update action.
4391
+ * @param marker - Optional sidebar marker; omit to leave unchanged.
4392
+ * @param parentUuid - Parent environment id; `null` clears; omit to leave unchanged.
4365
4393
  */
4366
- async updateEnvironment(id, name, variables, actingUserId, marker) {
4394
+ async updateEnvironment(id, name, variables, actingUserId, marker, parentUuid) {
4367
4395
  const trimmedName = trimRequiredName(name, "Environment name");
4368
4396
  const updatedAt = /* @__PURE__ */ new Date();
4369
- const result = marker !== void 0 ? await this.executeStatement(
4397
+ const setClauses = ["name = ?", "variables = ?", "updated_at = ?", "updated_by_user_id = ?"];
4398
+ const params = [
4399
+ trimmedName,
4400
+ JSON.stringify(variables),
4401
+ updatedAt,
4402
+ actingUserId
4403
+ ];
4404
+ if (marker !== void 0) {
4405
+ setClauses.push("marker = ?");
4406
+ params.push(serializeSidebarMarker(marker));
4407
+ }
4408
+ if (parentUuid !== void 0) {
4409
+ setClauses.push("parent_uuid = ?");
4410
+ params.push(parentUuid?.trim() || null);
4411
+ }
4412
+ params.push(id);
4413
+ const result = await this.executeStatement(
4370
4414
  `UPDATE environments
4371
- SET name = ?,
4372
- variables = ?,
4373
- updated_at = ?,
4374
- updated_by_user_id = ?,
4375
- marker = ?
4415
+ SET ${setClauses.join(",\n ")}
4376
4416
  WHERE id = ?`,
4377
- [
4378
- trimmedName,
4379
- JSON.stringify(variables),
4380
- updatedAt,
4381
- actingUserId,
4382
- serializeSidebarMarker(marker),
4383
- id
4384
- ]
4385
- ) : await this.executeStatement(
4386
- `UPDATE environments
4387
- SET name = ?,
4388
- variables = ?,
4389
- updated_at = ?,
4390
- updated_by_user_id = ?
4391
- WHERE id = ?`,
4392
- [trimmedName, JSON.stringify(variables), updatedAt, actingUserId, id]
4417
+ params
4393
4418
  );
4394
4419
  if ((result.affectedRows ?? 0) === 0) {
4395
4420
  throw new Error("Environment not found");
@@ -4406,13 +4431,17 @@ var MysqlDatabase = class _MysqlDatabase {
4406
4431
  return mapEnvironmentSqlRow(row);
4407
4432
  }
4408
4433
  /**
4409
- * Deletes an environment.
4434
+ * Deletes an environment and orphans any direct children (clears their parent_uuid).
4410
4435
  *
4411
4436
  * @param id - Environment ID to delete.
4412
4437
  * @param actingUserId - User performing the delete action.
4413
4438
  */
4414
4439
  async deleteEnvironment(id, actingUserId) {
4415
4440
  await this.recordAuditEntry(actingUserId, "delete", "environment", id);
4441
+ await this.executeStatement(
4442
+ "UPDATE environments SET parent_uuid = NULL WHERE parent_uuid = ?",
4443
+ [id]
4444
+ );
4416
4445
  await this.executeStatement("DELETE FROM environments WHERE id = ?", [id]);
4417
4446
  }
4418
4447
  /**
@@ -5756,7 +5785,8 @@ CREATE TABLE IF NOT EXISTS environments (
5756
5785
  created_at TIMESTAMPTZ NOT NULL,
5757
5786
  updated_at TIMESTAMPTZ NOT NULL,
5758
5787
  created_by_user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
5759
- updated_by_user_id TEXT REFERENCES users(id) ON DELETE SET NULL
5788
+ updated_by_user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
5789
+ parent_uuid TEXT REFERENCES environments(id) ON DELETE SET NULL
5760
5790
  );
5761
5791
  `.trim();
5762
5792
  var SNIPPETS_MIGRATION_SQL2 = `
@@ -6022,6 +6052,10 @@ var FOLDERS_MARKER_MIGRATION_SQL = buildMarkerMigrationSql("folders");
6022
6052
  var REQUESTS_MARKER_MIGRATION_SQL = buildMarkerMigrationSql("requests");
6023
6053
  var DOCUMENTS_MARKER_MIGRATION_SQL = buildMarkerMigrationSql("documents");
6024
6054
  var ENVIRONMENTS_MARKER_MIGRATION_SQL = buildMarkerMigrationSql("environments");
6055
+ var ENVIRONMENTS_PARENT_UUID_MIGRATION_SQL2 = `
6056
+ ALTER TABLE environments
6057
+ ADD COLUMN IF NOT EXISTS parent_uuid TEXT REFERENCES environments(id) ON DELETE SET NULL;
6058
+ `.trim();
6025
6059
  var POSTGRES_MIGRATIONS = [
6026
6060
  USERS_MIGRATION_SQL2,
6027
6061
  API_TOKENS_MIGRATION_SQL2,
@@ -6056,7 +6090,8 @@ var POSTGRES_MIGRATIONS = [
6056
6090
  FOLDERS_MARKER_MIGRATION_SQL,
6057
6091
  REQUESTS_MARKER_MIGRATION_SQL,
6058
6092
  DOCUMENTS_MARKER_MIGRATION_SQL,
6059
- ENVIRONMENTS_MARKER_MIGRATION_SQL
6093
+ ENVIRONMENTS_MARKER_MIGRATION_SQL,
6094
+ ENVIRONMENTS_PARENT_UUID_MIGRATION_SQL2
6060
6095
  ];
6061
6096
 
6062
6097
  // src/db/postgres/schemas.ts
@@ -7010,37 +7045,36 @@ var PostgresDatabase = class _PostgresDatabase {
7010
7045
  return mapEnvironmentSqlRow(row);
7011
7046
  }
7012
7047
  /**
7013
- * Updates an environment's name and variables.
7048
+ * Updates an environment's name, variables, and optional parent link.
7014
7049
  *
7015
7050
  * @param actingUserId - User performing the update action.
7051
+ * @param marker - Optional sidebar marker; omit to leave unchanged.
7052
+ * @param parentUuid - Parent environment id; `null` clears; omit to leave unchanged.
7016
7053
  */
7017
- async updateEnvironment(id, name, variables, actingUserId, marker) {
7054
+ async updateEnvironment(id, name, variables, actingUserId, marker, parentUuid) {
7018
7055
  const trimmedName = trimRequiredName(name, "Environment name");
7019
7056
  const updatedAt = /* @__PURE__ */ new Date();
7020
- const result = marker !== void 0 ? await this.query(
7021
- `UPDATE environments
7022
- SET name = $1,
7023
- variables = $2,
7024
- updated_at = $3,
7025
- updated_by_user_id = $4,
7026
- marker = $5
7027
- WHERE id = $6`,
7028
- [
7029
- trimmedName,
7030
- JSON.stringify(variables),
7031
- updatedAt,
7032
- actingUserId,
7033
- serializeSidebarMarker(marker),
7034
- id
7035
- ]
7036
- ) : await this.query(
7057
+ const setClauses = [
7058
+ "name = $1",
7059
+ "variables = $2",
7060
+ "updated_at = $3",
7061
+ "updated_by_user_id = $4"
7062
+ ];
7063
+ const params = [trimmedName, JSON.stringify(variables), updatedAt, actingUserId];
7064
+ if (marker !== void 0) {
7065
+ params.push(serializeSidebarMarker(marker));
7066
+ setClauses.push(`marker = $${params.length}`);
7067
+ }
7068
+ if (parentUuid !== void 0) {
7069
+ params.push(parentUuid?.trim() || null);
7070
+ setClauses.push(`parent_uuid = $${params.length}`);
7071
+ }
7072
+ params.push(id);
7073
+ const result = await this.query(
7037
7074
  `UPDATE environments
7038
- SET name = $1,
7039
- variables = $2,
7040
- updated_at = $3,
7041
- updated_by_user_id = $4
7042
- WHERE id = $5`,
7043
- [trimmedName, JSON.stringify(variables), updatedAt, actingUserId, id]
7075
+ SET ${setClauses.join(",\n ")}
7076
+ WHERE id = $${params.length}`,
7077
+ params
7044
7078
  );
7045
7079
  if ((result.rowCount ?? 0) === 0) {
7046
7080
  throw new Error("Environment not found");
@@ -7057,13 +7091,14 @@ var PostgresDatabase = class _PostgresDatabase {
7057
7091
  return mapEnvironmentSqlRow(row);
7058
7092
  }
7059
7093
  /**
7060
- * Deletes an environment.
7094
+ * Deletes an environment and orphans any direct children (clears their parent_uuid).
7061
7095
  *
7062
7096
  * @param id - Environment ID to delete.
7063
7097
  * @param actingUserId - User performing the delete action.
7064
7098
  */
7065
7099
  async deleteEnvironment(id, actingUserId) {
7066
7100
  await this.recordAuditEntry(actingUserId, "delete", "environment", id);
7101
+ await this.query("UPDATE environments SET parent_uuid = NULL WHERE parent_uuid = $1", [id]);
7067
7102
  await this.query("DELETE FROM environments WHERE id = $1", [id]);
7068
7103
  }
7069
7104
  /**
@@ -9475,6 +9510,7 @@ var variableSchema = z5.object({
9475
9510
  key: z5.string(),
9476
9511
  value: z5.string(),
9477
9512
  defaultValue: z5.string(),
9513
+ enabled: z5.boolean().optional().default(true),
9478
9514
  share: z5.boolean()
9479
9515
  });
9480
9516
  var authConfigSchema = z5.object({
@@ -9664,7 +9700,8 @@ var environmentRecordSchema = z8.object({
9664
9700
  createdByUserId: z8.string().nullable(),
9665
9701
  updatedByUserId: z8.string().nullable(),
9666
9702
  deletionLocked: z8.boolean(),
9667
- marker: z8.string().nullable()
9703
+ marker: z8.string().nullable(),
9704
+ parentUuid: z8.string().nullable()
9668
9705
  });
9669
9706
  var snippetScopeSchema = z8.enum(["pre-request", "post-request", "any"]);
9670
9707
  var snippetRecordSchema = z8.object({
@@ -9733,7 +9770,8 @@ var createEnvironmentBodySchema = z8.object({
9733
9770
  var updateEnvironmentBodySchema = z8.object({
9734
9771
  name: z8.string().trim().min(1),
9735
9772
  variables: z8.array(variableSchema),
9736
- marker: sidebarMarkerBodySchema
9773
+ marker: sidebarMarkerBodySchema,
9774
+ parentUuid: z8.union([z8.string().trim().min(1), z8.null()]).optional()
9737
9775
  });
9738
9776
  var createSnippetBodySchema = z8.object({
9739
9777
  name: z8.string().trim().min(1),
@@ -11776,7 +11814,8 @@ async function registerEnvironmentRoutes(app, db) {
11776
11814
  request.body.name,
11777
11815
  request.body.variables,
11778
11816
  user.id,
11779
- request.body.marker
11817
+ request.body.marker,
11818
+ request.body.parentUuid
11780
11819
  );
11781
11820
  return reply.send(serializeEnvironment(environment));
11782
11821
  } catch (error) {