@openhi/constructs 0.0.185 → 0.0.187
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/lib/{chunk-B6GCDQKX.mjs → chunk-HQ4CPUPH.mjs} +2 -2
- package/lib/{chunk-B6GCDQKX.mjs.map → chunk-HQ4CPUPH.mjs.map} +1 -1
- package/lib/data-store-postgres-replication.handler.js +1 -1
- package/lib/data-store-postgres-replication.handler.js.map +1 -1
- package/lib/data-store-postgres-replication.handler.mjs +1 -1
- package/lib/rest-api-lambda.handler.js +717 -385
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +718 -386
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/package.json +10 -10
|
@@ -63,7 +63,7 @@ function buildResourceSoftDeleteSql(schemaName) {
|
|
|
63
63
|
` AND LOWER(workspace_id) = LOWER($2)`,
|
|
64
64
|
` AND LOWER(resource_type) = LOWER($3)`,
|
|
65
65
|
` AND LOWER(resource_id) = LOWER($4)`,
|
|
66
|
-
` AND $5
|
|
66
|
+
` AND $5 >= version;`
|
|
67
67
|
].join("\n");
|
|
68
68
|
}
|
|
69
69
|
async function ensureSchemaBootstrap(client, schemaName) {
|
|
@@ -76,4 +76,4 @@ export {
|
|
|
76
76
|
buildResourceSoftDeleteSql,
|
|
77
77
|
ensureSchemaBootstrap
|
|
78
78
|
};
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
79
|
+
//# sourceMappingURL=chunk-HQ4CPUPH.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/postgres/data-store-postgres-schema.ts"],"sourcesContent":["import type { Pool, PoolClient } from \"pg\";\n\n/**\n * @see sites/www-docs/content/architecture/adr/2026-04-17-01-ad-hoc-query-support-fhir-api.md\n *\n * SQL strings for the Postgres replication tier (ADR 2026-04-17-01, phase 1):\n * the JSONB `resources` table that mirrors current FHIR resources from the\n * DynamoDB single-table store. Phase 1 covers replication only; query routing\n * and indexes for individual SearchParameters are deferred to a follow-on phase.\n *\n * The `version` column stores the resource's `vid` (a ULID — see\n * `data-entity-common.ts`). ULIDs are monotonically lexically sortable, so the\n * UPSERT/UPDATE guards use plain `>` text comparison to reject out-of-order\n * Kinesis deliveries.\n */\n\nexport const POSTGRES_REPLICATION_SCHEMA_VERSION = 1;\n\nconst SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;\n\n/**\n * Validate that a schema name is a safe Postgres identifier and quote it for\n * inclusion in DDL/DML. Throws on anything that doesn't match the lower-snake\n * pattern OpenHI uses for branch-derived schema names (e.g. `b_a1b2c3`).\n */\nexport function quoteSchemaIdentifier(schemaName: string): string {\n if (!SCHEMA_NAME_PATTERN.test(schemaName)) {\n throw new Error(\n `Invalid Postgres schema name: ${JSON.stringify(schemaName)}; expected /[a-z_][a-z0-9_]{0,62}/`,\n );\n }\n return `\"${schemaName}\"`;\n}\n\n/**\n * Build the bootstrap DDL as a list of independent statements. Use this form\n * when sending the DDL through a transport that does not accept multi-statement\n * SQL in a single call — most importantly the AWS RDS Data API's\n * `ExecuteStatement`, which the REST API runner uses.\n *\n * Each statement is fully self-contained and idempotent (`IF NOT EXISTS`),\n * so the array can be executed in order with no transaction wrapper.\n */\nexport function buildSchemaBootstrapStatements(\n schemaName: string,\n): ReadonlyArray<string> {\n const s = quoteSchemaIdentifier(schemaName);\n return [\n `CREATE SCHEMA IF NOT EXISTS ${s};`,\n [\n `CREATE TABLE IF NOT EXISTS ${s}.resources (`,\n ` tenant_id text NOT NULL,`,\n ` workspace_id text NOT NULL,`,\n ` resource_type text NOT NULL,`,\n ` resource_id text NOT NULL,`,\n ` version text NOT NULL,`,\n ` last_updated timestamptz NOT NULL,`,\n ` deleted_at timestamptz,`,\n ` resource jsonb NOT NULL,`,\n ` PRIMARY KEY (tenant_id, workspace_id, resource_type, resource_id)`,\n `);`,\n ].join(\"\\n\"),\n [\n `CREATE INDEX IF NOT EXISTS resources_jsonb_gin`,\n ` ON ${s}.resources USING gin (resource);`,\n ].join(\"\\n\"),\n [\n `CREATE INDEX IF NOT EXISTS resources_listing`,\n ` ON ${s}.resources (tenant_id, workspace_id, resource_type, last_updated);`,\n ].join(\"\\n\"),\n ];\n}\n\nexport function buildSchemaBootstrapSql(schemaName: string): string {\n return buildSchemaBootstrapStatements(schemaName).join(\"\\n\");\n}\n\n/**\n * INSERT/MODIFY UPSERT with a monotonic `version` (ULID) guard. If a record\n * arrives out of order — e.g. a retried Kinesis delivery for an older `vid`\n * after a newer one — the WHERE clause in DO UPDATE leaves the row unchanged.\n *\n * Param order: $1 tenant_id, $2 workspace_id, $3 resource_type, $4 resource_id,\n * $5 version, $6 last_updated, $7 resource (jsonb-serialized).\n */\nexport function buildResourceUpsertSql(schemaName: string): string {\n const s = quoteSchemaIdentifier(schemaName);\n return [\n `INSERT INTO ${s}.resources`,\n ` (tenant_id, workspace_id, resource_type, resource_id, version, last_updated, deleted_at, resource)`,\n `VALUES ($1, $2, $3, $4, $5, $6, NULL, $7::jsonb)`,\n `ON CONFLICT (tenant_id, workspace_id, resource_type, resource_id)`,\n `DO UPDATE SET`,\n ` version = EXCLUDED.version,`,\n ` last_updated = EXCLUDED.last_updated,`,\n ` deleted_at = NULL,`,\n ` resource = EXCLUDED.resource`,\n `WHERE EXCLUDED.version > ${s}.resources.version;`,\n ].join(\"\\n\");\n}\n\n/**\n * REMOVE soft-delete with the
|
|
1
|
+
{"version":3,"sources":["../src/components/postgres/data-store-postgres-schema.ts"],"sourcesContent":["import type { Pool, PoolClient } from \"pg\";\n\n/**\n * @see sites/www-docs/content/architecture/adr/2026-04-17-01-ad-hoc-query-support-fhir-api.md\n *\n * SQL strings for the Postgres replication tier (ADR 2026-04-17-01, phase 1):\n * the JSONB `resources` table that mirrors current FHIR resources from the\n * DynamoDB single-table store. Phase 1 covers replication only; query routing\n * and indexes for individual SearchParameters are deferred to a follow-on phase.\n *\n * The `version` column stores the resource's `vid` (a ULID — see\n * `data-entity-common.ts`). ULIDs are monotonically lexically sortable, so the\n * UPSERT/UPDATE guards use plain `>` text comparison to reject out-of-order\n * Kinesis deliveries.\n */\n\nexport const POSTGRES_REPLICATION_SCHEMA_VERSION = 1;\n\nconst SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;\n\n/**\n * Validate that a schema name is a safe Postgres identifier and quote it for\n * inclusion in DDL/DML. Throws on anything that doesn't match the lower-snake\n * pattern OpenHI uses for branch-derived schema names (e.g. `b_a1b2c3`).\n */\nexport function quoteSchemaIdentifier(schemaName: string): string {\n if (!SCHEMA_NAME_PATTERN.test(schemaName)) {\n throw new Error(\n `Invalid Postgres schema name: ${JSON.stringify(schemaName)}; expected /[a-z_][a-z0-9_]{0,62}/`,\n );\n }\n return `\"${schemaName}\"`;\n}\n\n/**\n * Build the bootstrap DDL as a list of independent statements. Use this form\n * when sending the DDL through a transport that does not accept multi-statement\n * SQL in a single call — most importantly the AWS RDS Data API's\n * `ExecuteStatement`, which the REST API runner uses.\n *\n * Each statement is fully self-contained and idempotent (`IF NOT EXISTS`),\n * so the array can be executed in order with no transaction wrapper.\n */\nexport function buildSchemaBootstrapStatements(\n schemaName: string,\n): ReadonlyArray<string> {\n const s = quoteSchemaIdentifier(schemaName);\n return [\n `CREATE SCHEMA IF NOT EXISTS ${s};`,\n [\n `CREATE TABLE IF NOT EXISTS ${s}.resources (`,\n ` tenant_id text NOT NULL,`,\n ` workspace_id text NOT NULL,`,\n ` resource_type text NOT NULL,`,\n ` resource_id text NOT NULL,`,\n ` version text NOT NULL,`,\n ` last_updated timestamptz NOT NULL,`,\n ` deleted_at timestamptz,`,\n ` resource jsonb NOT NULL,`,\n ` PRIMARY KEY (tenant_id, workspace_id, resource_type, resource_id)`,\n `);`,\n ].join(\"\\n\"),\n [\n `CREATE INDEX IF NOT EXISTS resources_jsonb_gin`,\n ` ON ${s}.resources USING gin (resource);`,\n ].join(\"\\n\"),\n [\n `CREATE INDEX IF NOT EXISTS resources_listing`,\n ` ON ${s}.resources (tenant_id, workspace_id, resource_type, last_updated);`,\n ].join(\"\\n\"),\n ];\n}\n\nexport function buildSchemaBootstrapSql(schemaName: string): string {\n return buildSchemaBootstrapStatements(schemaName).join(\"\\n\");\n}\n\n/**\n * INSERT/MODIFY UPSERT with a monotonic `version` (ULID) guard. If a record\n * arrives out of order — e.g. a retried Kinesis delivery for an older `vid`\n * after a newer one — the WHERE clause in DO UPDATE leaves the row unchanged.\n *\n * Param order: $1 tenant_id, $2 workspace_id, $3 resource_type, $4 resource_id,\n * $5 version, $6 last_updated, $7 resource (jsonb-serialized).\n */\nexport function buildResourceUpsertSql(schemaName: string): string {\n const s = quoteSchemaIdentifier(schemaName);\n return [\n `INSERT INTO ${s}.resources`,\n ` (tenant_id, workspace_id, resource_type, resource_id, version, last_updated, deleted_at, resource)`,\n `VALUES ($1, $2, $3, $4, $5, $6, NULL, $7::jsonb)`,\n `ON CONFLICT (tenant_id, workspace_id, resource_type, resource_id)`,\n `DO UPDATE SET`,\n ` version = EXCLUDED.version,`,\n ` last_updated = EXCLUDED.last_updated,`,\n ` deleted_at = NULL,`,\n ` resource = EXCLUDED.resource`,\n `WHERE EXCLUDED.version > ${s}.resources.version;`,\n ].join(\"\\n\");\n}\n\n/**\n * REMOVE soft-delete with a `>=` version guard. A REMOVE stream record's\n * OldImage carries the vid of the row being deleted — the SAME version the\n * last upsert wrote into this table — so the guard must accept equality or\n * every real-world delete is a no-op and the search tier keeps returning\n * deleted resources. The pairing with {@link buildResourceUpsertSql}'s\n * strict `>` guard means the delete wins version ties: a replayed upsert of\n * the deleted version cannot resurrect the row, while a genuine re-create\n * (newer vid) still clears `deleted_at`. A duplicate REMOVE re-applies the\n * same soft delete, which is idempotent. Param order matches\n * {@link buildResourceUpsertSql} for the first five params.\n *\n * Param order: $1 tenant_id, $2 workspace_id, $3 resource_type, $4 resource_id,\n * $5 version (incoming), $6 deleted_at.\n */\nexport function buildResourceSoftDeleteSql(schemaName: string): string {\n const s = quoteSchemaIdentifier(schemaName);\n // Every key column is matched case-insensitively so a REMOVE event whose\n // upstream-derived keys resolve to a different case than the existing row\n // still hits the row. Protects the soft-delete against any pipeline-side\n // casing drift on `tenant_id`, `workspace_id`, `resource_type`, or\n // `resource_id` between writers and removers.\n return [\n `UPDATE ${s}.resources`,\n `SET deleted_at = $6,`,\n ` version = $5`,\n `WHERE LOWER(tenant_id) = LOWER($1)`,\n ` AND LOWER(workspace_id) = LOWER($2)`,\n ` AND LOWER(resource_type) = LOWER($3)`,\n ` AND LOWER(resource_id) = LOWER($4)`,\n ` AND $5 >= version;`,\n ].join(\"\\n\");\n}\n\n/**\n * Run schema DDL idempotently. Safe to call from every Lambda cold start; the\n * `IF NOT EXISTS` clauses make repeats no-ops. Caller is responsible for\n * memoizing across invocations on the same warm container.\n */\nexport async function ensureSchemaBootstrap(\n client: Pool | PoolClient,\n schemaName: string,\n): Promise<void> {\n await client.query(buildSchemaBootstrapSql(schemaName));\n}\n"],"mappings":";AAkBA,IAAM,sBAAsB;AAOrB,SAAS,sBAAsB,YAA4B;AAChE,MAAI,CAAC,oBAAoB,KAAK,UAAU,GAAG;AACzC,UAAM,IAAI;AAAA,MACR,iCAAiC,KAAK,UAAU,UAAU,CAAC;AAAA,IAC7D;AAAA,EACF;AACA,SAAO,IAAI,UAAU;AACvB;AAWO,SAAS,+BACd,YACuB;AACvB,QAAM,IAAI,sBAAsB,UAAU;AAC1C,SAAO;AAAA,IACL,+BAA+B,CAAC;AAAA,IAChC;AAAA,MACE,8BAA8B,CAAC;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,IACX;AAAA,MACE;AAAA,MACA,QAAQ,CAAC;AAAA,IACX,EAAE,KAAK,IAAI;AAAA,IACX;AAAA,MACE;AAAA,MACA,QAAQ,CAAC;AAAA,IACX,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAEO,SAAS,wBAAwB,YAA4B;AAClE,SAAO,+BAA+B,UAAU,EAAE,KAAK,IAAI;AAC7D;AAUO,SAAS,uBAAuB,YAA4B;AACjE,QAAM,IAAI,sBAAsB,UAAU;AAC1C,SAAO;AAAA,IACL,eAAe,CAAC;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,4BAA4B,CAAC;AAAA,EAC/B,EAAE,KAAK,IAAI;AACb;AAiBO,SAAS,2BAA2B,YAA4B;AACrE,QAAM,IAAI,sBAAsB,UAAU;AAM1C,SAAO;AAAA,IACL,UAAU,CAAC;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAOA,eAAsB,sBACpB,QACA,YACe;AACf,QAAM,OAAO,MAAM,wBAAwB,UAAU,CAAC;AACxD;","names":[]}
|
|
@@ -828,7 +828,7 @@ function buildResourceSoftDeleteSql(schemaName) {
|
|
|
828
828
|
` AND LOWER(workspace_id) = LOWER($2)`,
|
|
829
829
|
` AND LOWER(resource_type) = LOWER($3)`,
|
|
830
830
|
` AND LOWER(resource_id) = LOWER($4)`,
|
|
831
|
-
` AND $5
|
|
831
|
+
` AND $5 >= version;`
|
|
832
832
|
].join("\n");
|
|
833
833
|
}
|
|
834
834
|
async function ensureSchemaBootstrap(client, schemaName) {
|