import * as plugins from './plugins.js'; import type { TControllerSessionId } from '../ts_interfaces/index.js'; const base64UrlPattern = /^[A-Za-z0-9_-]+$/; export interface IControllerSessionParentRelationshipDocument { id: string; issuerId: string; projectIdentityId: string; harnessId: TControllerSessionId['harnessId']; parentSessionIdentityId: string; childSessionIdentityId: string; createdAt: Date; } const isPlainObject = (valueArg: unknown): valueArg is Record => ( typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg) && ( Object.getPrototypeOf(valueArg) === Object.prototype || Object.getPrototypeOf(valueArg) === null ) ); const hasExactKeys = ( valueArg: Record, keysArg: readonly string[], ): boolean => { const keys = Object.keys(valueArg); return keys.length === keysArg.length && keys.every((key) => keysArg.includes(key)); }; const isBase64UrlBytes = (valueArg: unknown, byteLengthArg: number): valueArg is string => ( typeof valueArg === 'string' && base64UrlPattern.test(valueArg) && Buffer.from(valueArg, 'base64url').byteLength === byteLengthArg && Buffer.from(valueArg, 'base64url').toString('base64url') === valueArg ); const isValidDate = (valueArg: unknown): valueArg is Date => ( valueArg instanceof Date && Number.isFinite(valueArg.getTime()) && valueArg.getTime() >= 0 ); const digest = (valueArg: unknown): string => plugins.crypto .createHash('sha256') .update(JSON.stringify(valueArg), 'utf8') .digest('base64url'); export const controllerSessionParentRelationshipDocumentId = ( issuerIdArg: string, projectIdentityIdArg: string, harnessIdArg: TControllerSessionId['harnessId'], childSessionIdentityIdArg: string, ): string => digest([ 'controller-session-parent-relationship-v1', issuerIdArg, projectIdentityIdArg, harnessIdArg, childSessionIdentityIdArg, ]); export const assertControllerSessionParentRelationshipDocument = ( valueArg: unknown, ): asserts valueArg is IControllerSessionParentRelationshipDocument => { if ( !isPlainObject(valueArg) || !hasExactKeys(valueArg, [ 'id', 'issuerId', 'projectIdentityId', 'harnessId', 'parentSessionIdentityId', 'childSessionIdentityId', 'createdAt', ]) || !isBase64UrlBytes(valueArg.issuerId, 32) || !isBase64UrlBytes(valueArg.projectIdentityId, 16) || (valueArg.harnessId !== 'opencode' && valueArg.harnessId !== 'flex' && valueArg.harnessId !== 'codex') || !isBase64UrlBytes(valueArg.parentSessionIdentityId, 32) || !isBase64UrlBytes(valueArg.childSessionIdentityId, 32) || valueArg.parentSessionIdentityId === valueArg.childSessionIdentityId || valueArg.id !== controllerSessionParentRelationshipDocumentId( valueArg.issuerId, valueArg.projectIdentityId, valueArg.harnessId, valueArg.childSessionIdentityId, ) || !isValidDate(valueArg.createdAt) ) throw new Error('Invalid controller session parent relationship document.'); }; @plugins.smartdata.compoundIndex({ name: 'controller_session_parent_relationship_parent', key: { issuerId: 1, projectIdentityId: 1, harnessId: 1, parentSessionIdentityId: 1, id: 1, }, }) @plugins.smartdata.compoundIndex({ name: 'controller_session_parent_relationship_child', key: { issuerId: 1, projectIdentityId: 1, harnessId: 1, childSessionIdentityId: 1, id: 1, }, }) @plugins.smartdata.managed({ collectionName: 'agl_controller_session_parent_relationships' }) @plugins.smartdata.exactPersistence({ assertDocument: assertControllerSessionParentRelationshipDocument, }) export class ControllerSessionParentRelationshipModel extends plugins.smartdata.SmartDataDbDoc< ControllerSessionParentRelationshipModel, IControllerSessionParentRelationshipDocument > { declare static exact: plugins.smartdata.TExact; @plugins.smartdata.unI() public id!: string; @plugins.smartdata.svDb() public issuerId!: string; @plugins.smartdata.svDb() public projectIdentityId!: string; @plugins.smartdata.svDb() public harnessId!: TControllerSessionId['harnessId']; @plugins.smartdata.svDb() public parentSessionIdentityId!: string; @plugins.smartdata.svDb() public childSessionIdentityId!: string; @plugins.smartdata.svDb() public createdAt!: Date; }