@derwinjs/db 0.3.0 → 0.5.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.
- package/dist/project-profile-store.d.ts +31 -0
- package/dist/project-profile-store.d.ts.map +1 -0
- package/dist/project-profile-store.js +156 -0
- package/dist/project-profile-store.js.map +1 -0
- package/dist/rag-retriever.d.ts +44 -0
- package/dist/rag-retriever.d.ts.map +1 -0
- package/dist/rag-retriever.js +177 -0
- package/dist/rag-retriever.js.map +1 -0
- package/dist/scripts/smoke-auto-fix.js +2 -0
- package/dist/scripts/smoke-auto-fix.js.map +1 -1
- package/dist/scripts/smoke-learning-loop.js +2 -0
- package/dist/scripts/smoke-learning-loop.js.map +1 -1
- package/dist/scripts/smoke-seed-lifeline-profile.d.ts +25 -0
- package/dist/scripts/smoke-seed-lifeline-profile.d.ts.map +1 -0
- package/dist/scripts/smoke-seed-lifeline-profile.js +130 -0
- package/dist/scripts/smoke-seed-lifeline-profile.js.map +1 -0
- package/package.json +5 -4
- package/prisma/migrations/20260506180000_enable_pgvector_and_vector_column/migration.sql +12 -0
- package/prisma/schema.prisma +10 -7
- package/prisma/seed.ts +52 -70
- package/prisma-client/edge.js +3 -4
- package/prisma-client/index-browser.js +0 -1
- package/prisma-client/index.d.ts +0 -80
- package/prisma-client/index.js +3 -4
- package/prisma-client/package.json +1 -1
- package/prisma-client/schema.prisma +10 -7
- package/prisma-client/wasm.js +0 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint 4 Phase 5 smoke — exercises the ProjectProfileStore against the real
|
|
3
|
+
* local Postgres by upserting the canonical Lifeline Profile from JSON.
|
|
4
|
+
*
|
|
5
|
+
* Run: pnpm --filter @derwinjs/db smoke:seed-lifeline-profile
|
|
6
|
+
*
|
|
7
|
+
* Prereqs:
|
|
8
|
+
* - Local Postgres running on port 5433 (per Sprint 1 docker setup)
|
|
9
|
+
* - DATABASE_URL set in packages/db/.env
|
|
10
|
+
* - Migrations applied (Sprint 4 Phase 1 ProjectProfile model present)
|
|
11
|
+
* - Seed run at least once so the Lifeline project row exists
|
|
12
|
+
*
|
|
13
|
+
* What this proves:
|
|
14
|
+
* 1. lifeline-profile.json parses and conforms to PutProfileInput
|
|
15
|
+
* 2. createPrismaProjectProfileStore().putProfile round-trips entities,
|
|
16
|
+
* criticalFlows, glossary, dependencies, complianceTags
|
|
17
|
+
* 3. getProfile returns identical counts (no silent JSON column truncation)
|
|
18
|
+
*
|
|
19
|
+
* Idempotent: putProfile is upsert-shaped, so re-running just refreshes
|
|
20
|
+
* lastEvolvedAt/updatedAt without polluting the table. No cleanup needed.
|
|
21
|
+
*
|
|
22
|
+
* Prints "SPRINT4-PHASE5 PROFILE READY ✓" on success. Exits 1 on any failure.
|
|
23
|
+
*/
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
import { dirname, join } from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import { PrismaClient } from '../prisma.js';
|
|
28
|
+
import { createPrismaProjectProfileStore } from '../project-profile-store.js';
|
|
29
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
30
|
+
const __dirname = dirname(__filename);
|
|
31
|
+
const PROFILE_JSON_PATH = join(__dirname, '..', '..', 'seed-data', 'lifeline-profile.json');
|
|
32
|
+
const prisma = new PrismaClient();
|
|
33
|
+
const store = createPrismaProjectProfileStore({ prisma });
|
|
34
|
+
async function main() {
|
|
35
|
+
// ─── Step 1: locate Lifeline project ────────────────────────────────
|
|
36
|
+
const lifeline = await prisma.project.findUnique({ where: { slug: 'lifeline' } });
|
|
37
|
+
if (!lifeline) {
|
|
38
|
+
throw new Error("Lifeline project (slug='lifeline') not found. Run `pnpm --filter @derwinjs/db db:seed` first.");
|
|
39
|
+
}
|
|
40
|
+
console.log(`✓ Found Lifeline project (id=${lifeline.id})`);
|
|
41
|
+
// ─── Step 2: load + parse the JSON ──────────────────────────────────
|
|
42
|
+
const raw = readFileSync(PROFILE_JSON_PATH, 'utf-8');
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
const lastIngestedAt = new Date(parsed.lastIngestedAt);
|
|
45
|
+
if (Number.isNaN(lastIngestedAt.getTime())) {
|
|
46
|
+
throw new Error(`lifeline-profile.json has invalid lastIngestedAt: ${parsed.lastIngestedAt}`);
|
|
47
|
+
}
|
|
48
|
+
console.log(`✓ Loaded lifeline-profile.json from ${PROFILE_JSON_PATH}`);
|
|
49
|
+
const expectedEntities = parsed.domainOntology.entities.length;
|
|
50
|
+
const expectedFlows = parsed.criticalFlows.length;
|
|
51
|
+
const expectedGlossary = Object.keys(parsed.glossary).length;
|
|
52
|
+
const expectedDeps = parsed.dependencies?.length ?? 0;
|
|
53
|
+
const expectedTags = parsed.complianceTags?.length ?? 0;
|
|
54
|
+
console.log(` source counts → entities=${String(expectedEntities)} flows=${String(expectedFlows)} glossary=${String(expectedGlossary)} deps=${String(expectedDeps)} tags=${String(expectedTags)}`);
|
|
55
|
+
// ─── Step 3: putProfile (upsert) ────────────────────────────────────
|
|
56
|
+
const input = {
|
|
57
|
+
projectId: lifeline.id,
|
|
58
|
+
type: parsed.type,
|
|
59
|
+
domainOntology: parsed.domainOntology,
|
|
60
|
+
riskProfile: parsed.riskProfile,
|
|
61
|
+
criticalFlows: parsed.criticalFlows,
|
|
62
|
+
glossary: parsed.glossary,
|
|
63
|
+
dependencies: parsed.dependencies ?? [],
|
|
64
|
+
complianceTags: parsed.complianceTags ?? [],
|
|
65
|
+
ingestedDocsHash: parsed.ingestedDocsHash,
|
|
66
|
+
lastIngestedAt,
|
|
67
|
+
};
|
|
68
|
+
const { profile, ref } = await store.putProfile(input);
|
|
69
|
+
console.log(`✓ putProfile → id=${ref.id} projectId=${ref.projectId}`);
|
|
70
|
+
assertProfileMatches(profile, input, {
|
|
71
|
+
expectedEntities,
|
|
72
|
+
expectedFlows,
|
|
73
|
+
expectedGlossary,
|
|
74
|
+
expectedDeps,
|
|
75
|
+
expectedTags,
|
|
76
|
+
});
|
|
77
|
+
// ─── Step 4: getProfile round-trip ──────────────────────────────────
|
|
78
|
+
const fetched = await store.getProfile(lifeline.id);
|
|
79
|
+
if (!fetched) {
|
|
80
|
+
throw new Error('getProfile returned null immediately after putProfile');
|
|
81
|
+
}
|
|
82
|
+
console.log(`✓ getProfile → returned profile for projectId=${lifeline.id}`);
|
|
83
|
+
assertProfileMatches(fetched, input, {
|
|
84
|
+
expectedEntities,
|
|
85
|
+
expectedFlows,
|
|
86
|
+
expectedGlossary,
|
|
87
|
+
expectedDeps,
|
|
88
|
+
expectedTags,
|
|
89
|
+
});
|
|
90
|
+
console.log(' round-trip counts match input (no JSON column truncation)');
|
|
91
|
+
// ─── Step 5: cross-tenant negative ──────────────────────────────────
|
|
92
|
+
const wrongTenant = await store.getProfile('not-a-real-project-id-99999');
|
|
93
|
+
if (wrongTenant !== null) {
|
|
94
|
+
throw new Error('Tenant isolation broken: getProfile with unknown projectId returned a row.');
|
|
95
|
+
}
|
|
96
|
+
console.log('✓ getProfile (unknown projectId) → null (tenant isolation OK)');
|
|
97
|
+
console.log('\nSPRINT4-PHASE5 PROFILE READY ✓');
|
|
98
|
+
}
|
|
99
|
+
function assertProfileMatches(profile, input, counts) {
|
|
100
|
+
if (profile.type !== input.type) {
|
|
101
|
+
throw new Error(`type mismatch: expected ${input.type}, got ${profile.type}`);
|
|
102
|
+
}
|
|
103
|
+
if (profile.domainOntology.entities.length !== counts.expectedEntities) {
|
|
104
|
+
throw new Error(`entities count mismatch: expected ${String(counts.expectedEntities)}, got ${String(profile.domainOntology.entities.length)}`);
|
|
105
|
+
}
|
|
106
|
+
if (profile.criticalFlows.length !== counts.expectedFlows) {
|
|
107
|
+
throw new Error(`criticalFlows count mismatch: expected ${String(counts.expectedFlows)}, got ${String(profile.criticalFlows.length)}`);
|
|
108
|
+
}
|
|
109
|
+
if (Object.keys(profile.glossary).length !== counts.expectedGlossary) {
|
|
110
|
+
throw new Error(`glossary terms count mismatch: expected ${String(counts.expectedGlossary)}, got ${String(Object.keys(profile.glossary).length)}`);
|
|
111
|
+
}
|
|
112
|
+
if (profile.dependencies.length !== counts.expectedDeps) {
|
|
113
|
+
throw new Error(`dependencies count mismatch: expected ${String(counts.expectedDeps)}, got ${String(profile.dependencies.length)}`);
|
|
114
|
+
}
|
|
115
|
+
if (profile.complianceTags.length !== counts.expectedTags) {
|
|
116
|
+
throw new Error(`complianceTags count mismatch: expected ${String(counts.expectedTags)}, got ${String(profile.complianceTags.length)}`);
|
|
117
|
+
}
|
|
118
|
+
if (profile.ingestedDocsHash !== input.ingestedDocsHash) {
|
|
119
|
+
throw new Error(`ingestedDocsHash mismatch: expected ${input.ingestedDocsHash}, got ${profile.ingestedDocsHash}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
main()
|
|
123
|
+
.catch((err) => {
|
|
124
|
+
console.error('\n✗ Smoke failed:', err);
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
})
|
|
127
|
+
.finally(async () => {
|
|
128
|
+
await prisma.$disconnect();
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=smoke-seed-lifeline-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoke-seed-lifeline-profile.js","sourceRoot":"","sources":["../../src/scripts/smoke-seed-lifeline-profile.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@derwinjs/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Prisma schema + migrations for Derwin's own Postgres. 14 models, project-namespaced. Per ADR-0005. Ships its own generated Prisma client (multi-platform binaries) for cross-consumer compatibility.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@prisma/client": "^5.22.0",
|
|
36
|
-
"@derwinjs/core": "0.
|
|
37
|
-
"@derwinjs/sdk": "0.
|
|
36
|
+
"@derwinjs/core": "0.5.0",
|
|
37
|
+
"@derwinjs/sdk": "0.5.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@vitest/coverage-v8": "^2.1.9",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"smoke:qa-ticket-store": "tsx src/scripts/smoke-qa-ticket-store.ts",
|
|
65
65
|
"smoke:auto-fix": "tsx src/scripts/smoke-auto-fix.ts",
|
|
66
66
|
"smoke:learning-loop": "tsx src/scripts/smoke-learning-loop.ts",
|
|
67
|
-
"smoke:orchestration": "tsx src/scripts/smoke-orchestration.ts"
|
|
67
|
+
"smoke:orchestration": "tsx src/scripts/smoke-orchestration.ts",
|
|
68
|
+
"smoke:seed-lifeline-profile": "tsx src/scripts/smoke-seed-lifeline-profile.ts"
|
|
68
69
|
}
|
|
69
70
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- Sprint 5 Phase 1 — Enable pgvector extension + migrate RAGCorpus.embedding to vector(1536)
|
|
2
|
+
-- The corpus is empty at v0.4.0 — DROP+ADD is safe (no data loss).
|
|
3
|
+
-- Index uses HNSW with cosine ops for similarity search per pgvector docs.
|
|
4
|
+
|
|
5
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
6
|
+
|
|
7
|
+
ALTER TABLE "derwin"."rag_corpus" DROP COLUMN IF EXISTS "embedding";
|
|
8
|
+
ALTER TABLE "derwin"."rag_corpus" ADD COLUMN "embedding" vector(1536);
|
|
9
|
+
|
|
10
|
+
CREATE INDEX "rag_corpus_embedding_hnsw_idx"
|
|
11
|
+
ON "derwin"."rag_corpus"
|
|
12
|
+
USING hnsw ("embedding" vector_cosine_ops);
|
package/prisma/schema.prisma
CHANGED
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
// 2. A multi-project broken-fixture E2E that asserts isolation
|
|
12
12
|
// (added in Sprint 13 / QAP-130).
|
|
13
13
|
//
|
|
14
|
-
// pgvector convention:
|
|
15
|
-
//
|
|
16
|
-
//
|
|
14
|
+
// pgvector convention: Sprint 5 Phase 1 enabled the pgvector extension and
|
|
15
|
+
// migrated `RAGCorpus.embedding` to `vector(1536)`. The column is typed as
|
|
16
|
+
// `Unsupported("vector(1536)")` because Prisma 5.22.0 has no native vector
|
|
17
|
+
// type — reads/writes use raw SQL via `$queryRaw` / `$executeRaw`. The HNSW
|
|
18
|
+
// index with `vector_cosine_ops` lives in the migration SQL.
|
|
17
19
|
//
|
|
18
20
|
// Multi-schema: every model + enum is in the `derwin` Postgres schema (not
|
|
19
21
|
// `public`). This isolates Derwin's tables from each consumer's existing
|
|
@@ -494,16 +496,17 @@ model ClassificationTrust {
|
|
|
494
496
|
}
|
|
495
497
|
|
|
496
498
|
// Successful past fixes used as in-context examples for new dispatches.
|
|
497
|
-
// Embedding column is
|
|
499
|
+
// Embedding column is `vector(1536)` (pgvector); Sprint 5 Phase 1 enabled the
|
|
500
|
+
// extension and migrated from `Bytes?`. See file header for query convention.
|
|
498
501
|
model RAGCorpus {
|
|
499
502
|
id String @id @default(cuid())
|
|
500
503
|
projectId String
|
|
501
504
|
classification String
|
|
502
505
|
surface SurfaceCategory
|
|
503
506
|
|
|
504
|
-
promptText String
|
|
505
|
-
diff String
|
|
506
|
-
embedding
|
|
507
|
+
promptText String @db.Text // the prompt that produced the fix
|
|
508
|
+
diff String @db.Text // the unified diff that worked
|
|
509
|
+
embedding Unsupported("vector(1536)")? // pgvector — Sprint 5 Phase 1 enabled the extension. Queries via raw SQL because Prisma 5.22.0 doesn't have native vector type support.
|
|
507
510
|
|
|
508
511
|
fixAttemptId String // back-reference to the source attempt
|
|
509
512
|
outcomeQuality Float // priority weight; declines if later usage shows problems
|
package/prisma/seed.ts
CHANGED
|
@@ -6,15 +6,39 @@
|
|
|
6
6
|
* minimal — just one Project (Lifeline) with its first ProjectProfile, a
|
|
7
7
|
* default Policy, and a single SpendLedger row to verify FKs work.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Profile content lives in `seed-data/lifeline-profile.json` (Sprint 4
|
|
10
|
+
* Phase 5). The JSON is the canonical Lifeline Profile authored against the
|
|
11
|
+
* product brief §7.1 and is hot-swappable when ingestion (QAP-047) ships.
|
|
12
12
|
*
|
|
13
|
-
* Status: scaffolded in QAP-005
|
|
13
|
+
* Status: scaffolded in QAP-005; data externalized to JSON in Sprint 4 Phase 5.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { dirname, join } from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
|
|
16
20
|
import { PrismaClient, ProjectMode, ProjectType } from '../src/prisma.js';
|
|
17
21
|
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = dirname(__filename);
|
|
24
|
+
|
|
25
|
+
interface LifelineProfileSeed {
|
|
26
|
+
type: string;
|
|
27
|
+
domainOntology: unknown;
|
|
28
|
+
riskProfile: unknown;
|
|
29
|
+
criticalFlows: unknown;
|
|
30
|
+
glossary: unknown;
|
|
31
|
+
dependencies: unknown;
|
|
32
|
+
complianceTags: string[];
|
|
33
|
+
ingestedDocsHash: string;
|
|
34
|
+
lastIngestedAt: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const lifelineProfilePath = join(__dirname, '..', 'seed-data', 'lifeline-profile.json');
|
|
38
|
+
const lifelineProfileData = JSON.parse(
|
|
39
|
+
readFileSync(lifelineProfilePath, 'utf-8'),
|
|
40
|
+
) as LifelineProfileSeed;
|
|
41
|
+
|
|
18
42
|
const prisma = new PrismaClient();
|
|
19
43
|
|
|
20
44
|
async function main() {
|
|
@@ -36,75 +60,33 @@ async function main() {
|
|
|
36
60
|
});
|
|
37
61
|
console.log(` ✓ Project: ${lifeline.slug} (${lifeline.id})`);
|
|
38
62
|
|
|
39
|
-
// ─── 2. Profile (
|
|
63
|
+
// ─── 2. Profile (data sourced from seed-data/lifeline-profile.json) ──
|
|
64
|
+
const lastIngestedAt = new Date(lifelineProfileData.lastIngestedAt);
|
|
40
65
|
const profile = await prisma.projectProfile.upsert({
|
|
41
66
|
where: { projectId: lifeline.id },
|
|
42
|
-
update: {
|
|
67
|
+
update: {
|
|
68
|
+
type: lifelineProfileData.type as ProjectType,
|
|
69
|
+
domainOntology: lifelineProfileData.domainOntology as object,
|
|
70
|
+
riskProfile: lifelineProfileData.riskProfile as object,
|
|
71
|
+
criticalFlows: lifelineProfileData.criticalFlows as object,
|
|
72
|
+
glossary: lifelineProfileData.glossary as object,
|
|
73
|
+
dependencies: lifelineProfileData.dependencies as object,
|
|
74
|
+
complianceTags: lifelineProfileData.complianceTags,
|
|
75
|
+
ingestedDocsHash: lifelineProfileData.ingestedDocsHash,
|
|
76
|
+
lastIngestedAt,
|
|
77
|
+
lastEvolvedAt: new Date(),
|
|
78
|
+
},
|
|
43
79
|
create: {
|
|
44
80
|
projectId: lifeline.id,
|
|
45
|
-
type: ProjectType
|
|
46
|
-
domainOntology:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'Location',
|
|
55
|
-
],
|
|
56
|
-
actions: ['book', 'arrive', 'sign', 'bill', 'refund', 'export'],
|
|
57
|
-
roles: ['admin', 'provider', 'patient', 'front-desk'],
|
|
58
|
-
},
|
|
59
|
-
// 8-vector risk weights — see product brief §3.1 + §7.1
|
|
60
|
-
riskProfile: {
|
|
61
|
-
codeHealth: 0.8,
|
|
62
|
-
functionalCorrectness: 1.0,
|
|
63
|
-
uiUxIntegrity: 0.5,
|
|
64
|
-
perfResilience: 0.7,
|
|
65
|
-
security: 0.9,
|
|
66
|
-
complianceAudit: 1.0, // HIPAA gates this to max
|
|
67
|
-
multiTenantSafety: 1.0,
|
|
68
|
-
operationalHealth: 0.7,
|
|
69
|
-
},
|
|
70
|
-
criticalFlows: [
|
|
71
|
-
{
|
|
72
|
-
name: 'patient-signup-to-first-booking',
|
|
73
|
-
steps: ['signup', 'verify-email', 'browse-services', 'book', 'confirmation'],
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'booking-to-arrived-to-encounter-to-signed-chart',
|
|
77
|
-
steps: ['arrive', 'check-in', 'encounter-start', 'chart-note', 'sign'],
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: 'chart-sign-to-bill-to-payment',
|
|
81
|
-
steps: ['sign', 'bill-create', 'payment-process', 'reconcile'],
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: 'admin-onboard-new-tenant',
|
|
85
|
-
steps: ['create-location', 'invite-providers', 'configure-services'],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'patient-data-export',
|
|
89
|
-
steps: ['request', 'verify-identity', 'package', 'deliver'],
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
glossary: {
|
|
93
|
-
Tenant: 'medspa Location (NOT customer org)',
|
|
94
|
-
Ticket: 'roadmap item (NOT support ticket)',
|
|
95
|
-
Patient: 'end-user receiving services',
|
|
96
|
-
Provider: 'licensed practitioner',
|
|
97
|
-
Encounter: 'a recorded patient-provider interaction',
|
|
98
|
-
},
|
|
99
|
-
dependencies: [
|
|
100
|
-
{ name: 'Vagaro', kind: 'scheduling', integration: 'cron-pull' },
|
|
101
|
-
{ name: 'Stripe', kind: 'payments', integration: 'webhook' },
|
|
102
|
-
{ name: 'Twilio', kind: 'sms', integration: 'webhook' },
|
|
103
|
-
{ name: 'Anthropic API', kind: 'llm', integration: 'http' },
|
|
104
|
-
],
|
|
105
|
-
complianceTags: ['HIPAA', 'SOC 2 (in progress)'],
|
|
106
|
-
ingestedDocsHash: 'seed-placeholder',
|
|
107
|
-
lastIngestedAt: new Date(),
|
|
81
|
+
type: lifelineProfileData.type as ProjectType,
|
|
82
|
+
domainOntology: lifelineProfileData.domainOntology as object,
|
|
83
|
+
riskProfile: lifelineProfileData.riskProfile as object,
|
|
84
|
+
criticalFlows: lifelineProfileData.criticalFlows as object,
|
|
85
|
+
glossary: lifelineProfileData.glossary as object,
|
|
86
|
+
dependencies: lifelineProfileData.dependencies as object,
|
|
87
|
+
complianceTags: lifelineProfileData.complianceTags,
|
|
88
|
+
ingestedDocsHash: lifelineProfileData.ingestedDocsHash,
|
|
89
|
+
lastIngestedAt,
|
|
108
90
|
lastEvolvedAt: new Date(),
|
|
109
91
|
},
|
|
110
92
|
});
|
|
@@ -168,7 +150,7 @@ async function main() {
|
|
|
168
150
|
});
|
|
169
151
|
console.log(` ✓ SpendLedger: seed marker`);
|
|
170
152
|
|
|
171
|
-
console.log('\nSeeded Lifeline.
|
|
153
|
+
console.log('\nSeeded Lifeline. Profile data: packages/db/seed-data/lifeline-profile.json');
|
|
172
154
|
}
|
|
173
155
|
|
|
174
156
|
main()
|