@grapity/grapity 0.1.0 → 0.3.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.
@@ -1635,9 +1635,9 @@ var RegistryService = class {
1635
1635
  return { spec, version, compatReport, isNewSpec };
1636
1636
  }
1637
1637
  async listSpecs(filters) {
1638
- const specs2 = await this.store.listSpecs(filters);
1638
+ const specs3 = await this.store.listSpecs(filters);
1639
1639
  return Promise.all(
1640
- specs2.map(async (spec) => {
1640
+ specs3.map(async (spec) => {
1641
1641
  const latestVersion = await this.store.getLatestVersion(spec.name);
1642
1642
  return { ...spec, latestVersion: latestVersion ?? void 0 };
1643
1643
  })
@@ -1895,8 +1895,8 @@ var listRoute = new Hono3().get("/", async (c) => {
1895
1895
  const type = c.req.query("type");
1896
1896
  const owner = c.req.query("owner");
1897
1897
  const tags = c.req.query("tags")?.split(",");
1898
- const specs2 = await service.listSpecs({ type, owner, tags });
1899
- return c.json({ data: specs2 });
1898
+ const specs3 = await service.listSpecs({ type, owner, tags });
1899
+ return c.json({ data: specs3 });
1900
1900
  });
1901
1901
 
1902
1902
  // src/registry/routes/get-spec.ts
@@ -2530,8 +2530,7 @@ function switchTab(tab) {
2530
2530
  }
2531
2531
  var welcomeRoute = new Hono12().get("/", (c) => {
2532
2532
  const config = c.get("config");
2533
- const mode = config.database === "sqlite" ? "local" : "remote";
2534
- return c.html(buildPage(config.port, mode));
2533
+ return c.html(buildPage(config.port, "local"));
2535
2534
  });
2536
2535
 
2537
2536
  // src/registry/routes/push-gateway-config.ts
@@ -2872,7 +2871,6 @@ var ingestGatewayLogRoute = new Hono18().post("/ingest/:provider/:environment",
2872
2871
  await service.ingestLog(provider, environment, payload);
2873
2872
  return c.json({ status: "ok" }, 201);
2874
2873
  } catch (err) {
2875
- console.error("Gateway log ingest error:", err);
2876
2874
  return c.json({
2877
2875
  error: "bad_request",
2878
2876
  message: err instanceof Error ? err.message : "Invalid log payload",
@@ -2985,9 +2983,7 @@ function createApp(config, store) {
2985
2983
  // src/registry/config.ts
2986
2984
  var defaultConfig = {
2987
2985
  port: 3750,
2988
- database: "sqlite",
2989
- sqlitePath: void 0,
2990
- gracePeriodDays: 30
2986
+ database: "sqlite"
2991
2987
  };
2992
2988
 
2993
2989
  // src/registry/storage/sqlite.ts
@@ -3493,27 +3489,529 @@ var SQLiteSpecStore = class {
3493
3489
  }
3494
3490
  };
3495
3491
 
3492
+ // src/registry/storage/postgresql.ts
3493
+ import { Pool } from "pg";
3494
+ import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
3495
+ import { migrate as migrate2 } from "drizzle-orm/node-postgres/migrator";
3496
+ import { eq as eq2, and as and2, desc as desc2, sql as sql2 } from "drizzle-orm";
3497
+
3498
+ // src/registry/storage/schema-pg.ts
3499
+ import { pgTable, text as text2, timestamp, boolean, jsonb, index as index2, integer as integer2 } from "drizzle-orm/pg-core";
3500
+ var specs2 = pgTable("specs", {
3501
+ id: text2("id").primaryKey(),
3502
+ name: text2("name").notNull().unique(),
3503
+ type: text2("type", { enum: ["openapi", "asyncapi"] }).notNull(),
3504
+ description: text2("description"),
3505
+ owner: text2("owner"),
3506
+ sourceRepo: text2("source_repo"),
3507
+ tags: jsonb("tags").$type().default([]),
3508
+ createdAt: timestamp("created_at").notNull(),
3509
+ updatedAt: timestamp("updated_at").notNull()
3510
+ });
3511
+ var specVersions2 = pgTable("spec_versions", {
3512
+ id: text2("id").primaryKey(),
3513
+ specId: text2("spec_id").notNull().references(() => specs2.id),
3514
+ semver: text2("semver").notNull(),
3515
+ content: text2("content").notNull(),
3516
+ checksum: text2("checksum").notNull(),
3517
+ gitRef: text2("git_ref"),
3518
+ pushedBy: text2("pushed_by"),
3519
+ compatibility: jsonb("compatibility").$type(),
3520
+ previousVersion: text2("previous_version"),
3521
+ forceReason: text2("force_reason"),
3522
+ isPrerelease: boolean("is_prerelease").notNull().default(false),
3523
+ createdAt: timestamp("created_at").notNull()
3524
+ }, (table) => [
3525
+ index2("idx_spec_versions_spec_id").on(table.specId),
3526
+ index2("idx_spec_versions_semver").on(table.specId, table.semver)
3527
+ ]);
3528
+ var auditLog2 = pgTable("audit_log", {
3529
+ id: text2("id").primaryKey(),
3530
+ action: text2("action", { enum: ["spec.push", "spec.push.force", "spec.delete"] }).notNull(),
3531
+ actor: text2("actor").notNull(),
3532
+ specName: text2("spec_name").notNull(),
3533
+ version: text2("version"),
3534
+ details: jsonb("details").$type(),
3535
+ createdAt: timestamp("created_at").notNull()
3536
+ }, (table) => [
3537
+ index2("idx_audit_log_spec_name").on(table.specName),
3538
+ index2("idx_audit_log_created_at").on(table.createdAt)
3539
+ ]);
3540
+ var gatewayConfigs2 = pgTable("gateway_configs", {
3541
+ id: text2("id").primaryKey(),
3542
+ name: text2("name").notNull().unique(),
3543
+ provider: text2("provider", { enum: ["kong"] }).notNull(),
3544
+ specName: text2("spec_name").notNull(),
3545
+ specSemver: text2("spec_semver").notNull(),
3546
+ createdAt: timestamp("created_at").notNull(),
3547
+ updatedAt: timestamp("updated_at").notNull()
3548
+ });
3549
+ var gatewayConfigVersions2 = pgTable("gateway_config_versions", {
3550
+ id: text2("id").primaryKey(),
3551
+ gatewayConfigId: text2("gateway_config_id").notNull().references(() => gatewayConfigs2.id),
3552
+ routes: jsonb("routes").$type().notNull(),
3553
+ environments: jsonb("environments").$type().notNull(),
3554
+ callerIdentification: jsonb("caller_identification").$type(),
3555
+ content: text2("content").notNull(),
3556
+ checksum: text2("checksum").notNull(),
3557
+ pushedBy: text2("pushed_by"),
3558
+ createdAt: timestamp("created_at").notNull()
3559
+ }, (table) => [
3560
+ index2("idx_gateway_config_versions_config_id").on(table.gatewayConfigId)
3561
+ ]);
3562
+ var httpLogs2 = pgTable("http_logs", {
3563
+ id: text2("id").primaryKey(),
3564
+ provider: text2("provider").notNull(),
3565
+ gatewayConfigName: text2("gateway_config_name").notNull(),
3566
+ environment: text2("environment").notNull(),
3567
+ method: text2("method").notNull(),
3568
+ path: text2("path").notNull(),
3569
+ routePath: text2("route_path"),
3570
+ status: integer2("status").notNull(),
3571
+ callerId: text2("caller_id"),
3572
+ callerSource: text2("caller_source"),
3573
+ callerConfidence: text2("caller_confidence").notNull(),
3574
+ occurredAt: timestamp("occurred_at").notNull(),
3575
+ createdAt: timestamp("created_at").notNull()
3576
+ }, (table) => [
3577
+ index2("idx_http_logs_config_env").on(table.gatewayConfigName, table.environment),
3578
+ index2("idx_http_logs_occurred_at").on(table.occurredAt),
3579
+ index2("idx_http_logs_caller").on(table.gatewayConfigName, table.environment, table.callerId)
3580
+ ]);
3581
+ var provisions2 = pgTable("provisions", {
3582
+ id: text2("id").primaryKey(),
3583
+ gatewayConfigName: text2("gateway_config_name").notNull(),
3584
+ gatewayConfigVersion: text2("gateway_config_version").notNull(),
3585
+ environment: text2("environment").notNull(),
3586
+ provider: text2("provider", { enum: ["kong"] }).notNull(),
3587
+ synced: boolean("synced").notNull().default(false),
3588
+ actor: text2("actor").notNull(),
3589
+ details: jsonb("details").$type(),
3590
+ createdAt: timestamp("created_at").notNull()
3591
+ }, (table) => [
3592
+ index2("idx_provisions_config_name").on(table.gatewayConfigName),
3593
+ index2("idx_provisions_created_at").on(table.createdAt)
3594
+ ]);
3595
+
3596
+ // src/registry/storage/postgresql.ts
3597
+ import { v4 as uuid6 } from "uuid";
3598
+ var MIGRATIONS_FOLDER2 = PG_MIGRATIONS_FOLDER;
3599
+ var PostgreSQLSpecStore = class {
3600
+ db;
3601
+ pool;
3602
+ constructor(postgresUrl) {
3603
+ this.pool = new Pool({ connectionString: postgresUrl });
3604
+ this.db = drizzle2(this.pool);
3605
+ }
3606
+ async migrate() {
3607
+ await migrate2(this.db, {
3608
+ migrationsFolder: MIGRATIONS_FOLDER2
3609
+ });
3610
+ }
3611
+ async end() {
3612
+ await this.pool.end();
3613
+ }
3614
+ async getSpec(name) {
3615
+ const rows = await this.db.select().from(specs2).where(eq2(specs2.name, name)).limit(1);
3616
+ if (rows.length === 0) return null;
3617
+ return this.mapSpecRow(rows[0]);
3618
+ }
3619
+ async getSpecVersion(name, semver) {
3620
+ const spec = await this.getSpec(name);
3621
+ if (!spec) return null;
3622
+ const rows = await this.db.select().from(specVersions2).where(and2(eq2(specVersions2.specId, spec.id), eq2(specVersions2.semver, semver))).limit(1);
3623
+ if (rows.length === 0) return null;
3624
+ return this.mapVersionRow(rows[0]);
3625
+ }
3626
+ async getLatestVersion(name) {
3627
+ const spec = await this.getSpec(name);
3628
+ if (!spec) return null;
3629
+ const rows = await this.db.select().from(specVersions2).where(eq2(specVersions2.specId, spec.id)).orderBy(desc2(specVersions2.createdAt), desc2(specVersions2.id)).limit(1);
3630
+ if (rows.length === 0) return null;
3631
+ return this.mapVersionRow(rows[0]);
3632
+ }
3633
+ async listSpecs(filters) {
3634
+ const conditions = [];
3635
+ if (filters?.type) conditions.push(eq2(specs2.type, filters.type));
3636
+ if (filters?.owner) conditions.push(eq2(specs2.owner, filters.owner));
3637
+ let rows = conditions.length > 0 ? await this.db.select().from(specs2).where(and2(...conditions)) : await this.db.select().from(specs2);
3638
+ if (filters?.tags && filters.tags.length > 0) {
3639
+ rows = rows.filter((row) => {
3640
+ const rowTags = row.tags ?? [];
3641
+ return filters.tags.every((tag) => rowTags.includes(tag));
3642
+ });
3643
+ }
3644
+ return rows.map((r) => this.mapSpecRow(r));
3645
+ }
3646
+ async listVersions(name, options) {
3647
+ const spec = await this.getSpec(name);
3648
+ if (!spec) return { versions: [], total: 0 };
3649
+ const limit = options?.limit ?? 10;
3650
+ const offset = options?.offset ?? 0;
3651
+ const [countRow] = await this.db.select({ count: sql2`count(*)` }).from(specVersions2).where(eq2(specVersions2.specId, spec.id));
3652
+ const total = Number(countRow?.count ?? 0);
3653
+ const rows = await this.db.select().from(specVersions2).where(eq2(specVersions2.specId, spec.id)).orderBy(desc2(specVersions2.createdAt), desc2(specVersions2.id)).limit(limit).offset(offset);
3654
+ return { versions: rows.map((r) => this.mapVersionRow(r)), total };
3655
+ }
3656
+ async pushSpecVersion(spec, version) {
3657
+ const existingSpec = await this.getSpec(spec.name);
3658
+ if (!existingSpec) {
3659
+ await this.db.insert(specs2).values({
3660
+ id: spec.id,
3661
+ name: spec.name,
3662
+ type: spec.type,
3663
+ description: spec.description ?? null,
3664
+ owner: spec.owner ?? null,
3665
+ sourceRepo: spec.sourceRepo ?? null,
3666
+ tags: spec.tags ?? [],
3667
+ createdAt: spec.createdAt,
3668
+ updatedAt: spec.updatedAt
3669
+ });
3670
+ } else {
3671
+ await this.db.update(specs2).set({ updatedAt: /* @__PURE__ */ new Date() }).where(eq2(specs2.id, existingSpec.id));
3672
+ }
3673
+ const specId = existingSpec?.id ?? spec.id;
3674
+ await this.db.insert(specVersions2).values({
3675
+ id: version.id,
3676
+ specId,
3677
+ semver: version.semver,
3678
+ content: version.content,
3679
+ checksum: version.checksum,
3680
+ gitRef: version.gitRef ?? null,
3681
+ pushedBy: version.pushedBy ?? null,
3682
+ compatibility: version.compatibility ?? null,
3683
+ previousVersion: version.previousVersion ?? null,
3684
+ forceReason: version.forceReason ?? null,
3685
+ isPrerelease: version.isPrerelease,
3686
+ createdAt: version.createdAt
3687
+ });
3688
+ return version;
3689
+ }
3690
+ async deleteSpec(name) {
3691
+ const existingSpec = await this.getSpec(name);
3692
+ if (!existingSpec) return false;
3693
+ await this.db.delete(specVersions2).where(eq2(specVersions2.specId, existingSpec.id));
3694
+ await this.db.delete(specs2).where(eq2(specs2.id, existingSpec.id));
3695
+ return true;
3696
+ }
3697
+ async getCompatReport(name, semver) {
3698
+ const version = await this.getSpecVersion(name, semver);
3699
+ return version?.compatibility ?? null;
3700
+ }
3701
+ async logAudit(action, actor, specName, version, details) {
3702
+ await this.db.insert(auditLog2).values({
3703
+ id: uuid6(),
3704
+ action,
3705
+ actor,
3706
+ specName,
3707
+ version: version ?? null,
3708
+ details: details ?? null,
3709
+ createdAt: /* @__PURE__ */ new Date()
3710
+ });
3711
+ }
3712
+ mapSpecRow(row) {
3713
+ return {
3714
+ id: row.id,
3715
+ name: row.name,
3716
+ type: row.type,
3717
+ description: row.description ?? void 0,
3718
+ owner: row.owner ?? void 0,
3719
+ sourceRepo: row.sourceRepo ?? void 0,
3720
+ tags: row.tags ?? [],
3721
+ createdAt: row.createdAt,
3722
+ updatedAt: row.updatedAt
3723
+ };
3724
+ }
3725
+ mapVersionRow(row) {
3726
+ return {
3727
+ id: row.id,
3728
+ specId: row.specId,
3729
+ semver: row.semver,
3730
+ content: row.content,
3731
+ checksum: row.checksum,
3732
+ gitRef: row.gitRef ?? void 0,
3733
+ pushedBy: row.pushedBy ?? void 0,
3734
+ compatibility: row.compatibility ?? void 0,
3735
+ previousVersion: row.previousVersion ?? void 0,
3736
+ forceReason: row.forceReason ?? void 0,
3737
+ isPrerelease: row.isPrerelease,
3738
+ createdAt: row.createdAt
3739
+ };
3740
+ }
3741
+ // GatewayConfigStore implementation
3742
+ async getGatewayConfig(name) {
3743
+ const rows = await this.db.select().from(gatewayConfigs2).where(eq2(gatewayConfigs2.name, name)).limit(1);
3744
+ if (rows.length === 0) return null;
3745
+ return this.mapGatewayConfigRow(rows[0]);
3746
+ }
3747
+ async listGatewayConfigs() {
3748
+ const rows = await this.db.select().from(gatewayConfigs2);
3749
+ return rows.map((r) => this.mapGatewayConfigRow(r));
3750
+ }
3751
+ async getGatewayConfigVersion(name, versionId) {
3752
+ const config = await this.getGatewayConfig(name);
3753
+ if (!config) return null;
3754
+ const rows = await this.db.select().from(gatewayConfigVersions2).where(and2(eq2(gatewayConfigVersions2.gatewayConfigId, config.id), eq2(gatewayConfigVersions2.id, versionId))).limit(1);
3755
+ if (rows.length === 0) return null;
3756
+ return this.mapGatewayConfigVersionRow(rows[0]);
3757
+ }
3758
+ async getLatestGatewayConfigVersion(name) {
3759
+ const config = await this.getGatewayConfig(name);
3760
+ if (!config) return null;
3761
+ const rows = await this.db.select().from(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.gatewayConfigId, config.id)).orderBy(desc2(gatewayConfigVersions2.createdAt), desc2(gatewayConfigVersions2.id)).limit(1);
3762
+ if (rows.length === 0) return null;
3763
+ return this.mapGatewayConfigVersionRow(rows[0]);
3764
+ }
3765
+ async listGatewayConfigVersions(name) {
3766
+ const config = await this.getGatewayConfig(name);
3767
+ if (!config) return [];
3768
+ const rows = await this.db.select().from(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.gatewayConfigId, config.id)).orderBy(desc2(gatewayConfigVersions2.createdAt), desc2(gatewayConfigVersions2.id)).limit(5);
3769
+ return rows.map((r) => this.mapGatewayConfigVersionRow(r));
3770
+ }
3771
+ async pushGatewayConfigVersion(config, version) {
3772
+ const existingConfig = await this.getGatewayConfig(config.name);
3773
+ if (!existingConfig) {
3774
+ await this.db.insert(gatewayConfigs2).values({
3775
+ id: config.id,
3776
+ name: config.name,
3777
+ provider: config.provider,
3778
+ specName: config.specName,
3779
+ specSemver: config.specSemver,
3780
+ createdAt: config.createdAt,
3781
+ updatedAt: config.updatedAt
3782
+ });
3783
+ } else {
3784
+ await this.db.update(gatewayConfigs2).set({ updatedAt: /* @__PURE__ */ new Date(), specSemver: config.specSemver }).where(eq2(gatewayConfigs2.id, existingConfig.id));
3785
+ }
3786
+ const configId = existingConfig?.id ?? config.id;
3787
+ await this.db.insert(gatewayConfigVersions2).values({
3788
+ id: version.id,
3789
+ gatewayConfigId: configId,
3790
+ routes: version.routes,
3791
+ environments: version.environments,
3792
+ callerIdentification: version.callerIdentification ?? null,
3793
+ content: version.content,
3794
+ checksum: version.checksum,
3795
+ pushedBy: version.pushedBy ?? null,
3796
+ createdAt: version.createdAt
3797
+ });
3798
+ const versions = await this.db.select().from(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.gatewayConfigId, configId)).orderBy(desc2(gatewayConfigVersions2.createdAt), desc2(gatewayConfigVersions2.id));
3799
+ if (versions.length > 5) {
3800
+ const toDelete = versions.slice(5);
3801
+ for (const v of toDelete) {
3802
+ await this.db.delete(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.id, v.id));
3803
+ }
3804
+ }
3805
+ return version;
3806
+ }
3807
+ async recordProvision(provision) {
3808
+ await this.db.insert(provisions2).values({
3809
+ id: provision.id,
3810
+ gatewayConfigName: provision.gatewayConfigName,
3811
+ gatewayConfigVersion: provision.gatewayConfigVersion,
3812
+ environment: provision.environment,
3813
+ provider: provision.provider,
3814
+ synced: provision.synced,
3815
+ actor: provision.actor,
3816
+ details: provision.details ?? null,
3817
+ createdAt: provision.createdAt
3818
+ });
3819
+ }
3820
+ async listProvisions(gatewayConfigName) {
3821
+ const rows = gatewayConfigName ? await this.db.select().from(provisions2).where(eq2(provisions2.gatewayConfigName, gatewayConfigName)).orderBy(desc2(provisions2.createdAt)) : await this.db.select().from(provisions2).orderBy(desc2(provisions2.createdAt));
3822
+ return rows.map((r) => ({
3823
+ id: r.id,
3824
+ gatewayConfigName: r.gatewayConfigName,
3825
+ gatewayConfigVersion: r.gatewayConfigVersion,
3826
+ environment: r.environment,
3827
+ provider: r.provider,
3828
+ synced: r.synced,
3829
+ actor: r.actor,
3830
+ details: r.details ?? void 0,
3831
+ createdAt: r.createdAt
3832
+ }));
3833
+ }
3834
+ mapGatewayConfigRow(row) {
3835
+ return {
3836
+ id: row.id,
3837
+ name: row.name,
3838
+ provider: row.provider,
3839
+ specName: row.specName,
3840
+ specSemver: row.specSemver,
3841
+ createdAt: row.createdAt,
3842
+ updatedAt: row.updatedAt
3843
+ };
3844
+ }
3845
+ mapGatewayConfigVersionRow(row) {
3846
+ return {
3847
+ id: row.id,
3848
+ gatewayConfigId: row.gatewayConfigId,
3849
+ routes: row.routes,
3850
+ environments: row.environments,
3851
+ callerIdentification: row.callerIdentification ?? void 0,
3852
+ content: row.content,
3853
+ checksum: row.checksum,
3854
+ pushedBy: row.pushedBy ?? void 0,
3855
+ createdAt: row.createdAt
3856
+ };
3857
+ }
3858
+ async recordGatewayLog(log) {
3859
+ await this.db.insert(httpLogs2).values({
3860
+ id: log.id,
3861
+ provider: log.provider,
3862
+ gatewayConfigName: log.gatewayConfigName,
3863
+ environment: log.environment,
3864
+ method: log.method,
3865
+ path: log.path,
3866
+ routePath: log.routePath ?? null,
3867
+ status: log.status,
3868
+ callerId: log.callerId ?? null,
3869
+ callerSource: log.callerSource ?? null,
3870
+ callerConfidence: log.callerConfidence,
3871
+ occurredAt: log.occurredAt,
3872
+ createdAt: log.createdAt
3873
+ });
3874
+ }
3875
+ async listGatewayLogs(filters) {
3876
+ const limit = filters.limit ?? 50;
3877
+ const offset = filters.offset ?? 0;
3878
+ let query = this.db.select().from(httpLogs2);
3879
+ const conditions = [];
3880
+ if (filters.gatewayConfigName) {
3881
+ conditions.push(eq2(httpLogs2.gatewayConfigName, filters.gatewayConfigName));
3882
+ }
3883
+ if (filters.environment) {
3884
+ conditions.push(eq2(httpLogs2.environment, filters.environment));
3885
+ }
3886
+ if (filters.path) {
3887
+ conditions.push(eq2(httpLogs2.path, filters.path));
3888
+ }
3889
+ if (filters.method) {
3890
+ conditions.push(eq2(httpLogs2.method, filters.method));
3891
+ }
3892
+ if (filters.status !== void 0) {
3893
+ conditions.push(eq2(httpLogs2.status, filters.status));
3894
+ }
3895
+ if (filters.from) {
3896
+ conditions.push(sql2`${httpLogs2.occurredAt} >= ${filters.from}`);
3897
+ }
3898
+ if (filters.to) {
3899
+ conditions.push(sql2`${httpLogs2.occurredAt} <= ${filters.to}`);
3900
+ }
3901
+ if (conditions.length > 0) {
3902
+ query = query.where(and2(...conditions));
3903
+ }
3904
+ const countResult = await this.db.select({ count: sql2`count(*)` }).from(httpLogs2).where(conditions.length > 0 ? and2(...conditions) : void 0);
3905
+ const total = countResult[0]?.count ?? 0;
3906
+ const rows = await query.orderBy(desc2(httpLogs2.occurredAt)).limit(limit).offset(offset);
3907
+ return {
3908
+ logs: rows.map((r) => ({
3909
+ id: r.id,
3910
+ provider: r.provider,
3911
+ gatewayConfigName: r.gatewayConfigName,
3912
+ environment: r.environment,
3913
+ method: r.method,
3914
+ path: r.path,
3915
+ routePath: r.routePath ?? void 0,
3916
+ status: r.status,
3917
+ callerId: r.callerId ?? void 0,
3918
+ callerSource: r.callerSource ?? void 0,
3919
+ callerConfidence: r.callerConfidence,
3920
+ occurredAt: r.occurredAt,
3921
+ createdAt: r.createdAt
3922
+ })),
3923
+ total
3924
+ };
3925
+ }
3926
+ async getGatewayLog(id) {
3927
+ const rows = await this.db.select().from(httpLogs2).where(eq2(httpLogs2.id, id)).limit(1);
3928
+ if (rows.length === 0) return null;
3929
+ const r = rows[0];
3930
+ return {
3931
+ id: r.id,
3932
+ provider: r.provider,
3933
+ gatewayConfigName: r.gatewayConfigName,
3934
+ environment: r.environment,
3935
+ method: r.method,
3936
+ path: r.path,
3937
+ routePath: r.routePath ?? void 0,
3938
+ status: r.status,
3939
+ callerId: r.callerId ?? void 0,
3940
+ callerSource: r.callerSource ?? void 0,
3941
+ callerConfidence: r.callerConfidence,
3942
+ occurredAt: r.occurredAt,
3943
+ createdAt: r.createdAt
3944
+ };
3945
+ }
3946
+ async getGatewayLogStats(_filters) {
3947
+ const conditions = [];
3948
+ if (_filters.gatewayConfigName) {
3949
+ conditions.push(eq2(httpLogs2.gatewayConfigName, _filters.gatewayConfigName));
3950
+ }
3951
+ if (_filters.environment) {
3952
+ conditions.push(eq2(httpLogs2.environment, _filters.environment));
3953
+ }
3954
+ if (_filters.from) {
3955
+ conditions.push(sql2`${httpLogs2.occurredAt} >= ${_filters.from}`);
3956
+ }
3957
+ if (_filters.to) {
3958
+ conditions.push(sql2`${httpLogs2.occurredAt} <= ${_filters.to}`);
3959
+ }
3960
+ const whereClause = conditions.length > 0 ? and2(...conditions) : void 0;
3961
+ const rows = await this.db.select({
3962
+ gatewayConfigName: httpLogs2.gatewayConfigName,
3963
+ environment: httpLogs2.environment,
3964
+ method: httpLogs2.method,
3965
+ routePath: httpLogs2.routePath,
3966
+ lastSeenAt: sql2`max(${httpLogs2.occurredAt})`,
3967
+ totalCalls: sql2`count(*)`,
3968
+ uniqueCallerIds: sql2`count(distinct ${httpLogs2.callerId})`
3969
+ }).from(httpLogs2).where(whereClause).groupBy(httpLogs2.gatewayConfigName, httpLogs2.environment, httpLogs2.method, httpLogs2.routePath);
3970
+ return rows.map((r) => ({
3971
+ gatewayConfigName: r.gatewayConfigName,
3972
+ environment: r.environment,
3973
+ method: r.method,
3974
+ routePath: r.routePath ?? "/",
3975
+ lastSeenAt: new Date(r.lastSeenAt),
3976
+ totalCalls: r.totalCalls,
3977
+ uniqueCallerIds: r.uniqueCallerIds
3978
+ }));
3979
+ }
3980
+ async deleteGatewayLogsOlderThan(days) {
3981
+ const cutoff = /* @__PURE__ */ new Date();
3982
+ cutoff.setDate(cutoff.getDate() - days);
3983
+ await this.db.delete(httpLogs2).where(sql2`${httpLogs2.occurredAt} < ${cutoff}`);
3984
+ }
3985
+ };
3986
+
3496
3987
  // src/registry/serve.ts
3497
3988
  async function startServer(userConfig) {
3498
3989
  const config = { ...defaultConfig, ...userConfig };
3499
- if (!config.sqlitePath && config.database === "sqlite") {
3500
- const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
3501
- config.sqlitePath = path.join(homeDir, ".grapity", "registry.db");
3502
- }
3503
- if (config.database === "sqlite" && config.sqlitePath) {
3504
- const dir = path.dirname(config.sqlitePath);
3990
+ let store;
3991
+ if (config.database === "postgresql") {
3992
+ if (!config.postgresUrl) {
3993
+ throw new Error("PostgreSQL database requested but no postgresUrl provided.");
3994
+ }
3995
+ store = new PostgreSQLSpecStore(config.postgresUrl);
3996
+ } else {
3997
+ const sqlitePath = config.sqlitePath ?? path.join(
3998
+ process.env.HOME || process.env.USERPROFILE || ".",
3999
+ ".grapity",
4000
+ "registry.db"
4001
+ );
4002
+ const dir = path.dirname(sqlitePath);
3505
4003
  if (!fs.existsSync(dir)) {
3506
4004
  fs.mkdirSync(dir, { recursive: true });
3507
4005
  }
4006
+ store = new SQLiteSpecStore(sqlitePath);
3508
4007
  }
3509
- const store = new SQLiteSpecStore(config.sqlitePath);
3510
4008
  await store.migrate();
3511
4009
  const app = createApp(config, store);
3512
- serve({
4010
+ const server = serve({
3513
4011
  fetch: app.fetch,
3514
4012
  port: config.port
3515
4013
  });
3516
- return app;
4014
+ return { app, store, server };
3517
4015
  }
3518
4016
  if (process.argv[1] === new URL(import.meta.url).pathname) {
3519
4017
  startServer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grapity/grapity",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "grapity - API spec registry and compatibility guardian",
6
6
  "license": "Apache-2.0",
@@ -59,10 +59,6 @@
59
59
  "typecheck": "tsc --noEmit",
60
60
  "test": "bun test",
61
61
  "generate": "bun run scripts/generate-types.ts",
62
- "db:generate:sqlite": "drizzle-kit generate --config drizzle/config.sqlite.ts",
63
- "db:generate:pg": "drizzle-kit generate --config drizzle/config.pg.ts",
64
- "db:migrate:sqlite": "drizzle-kit migrate --config drizzle/config.sqlite.ts",
65
- "db:migrate:pg": "drizzle-kit migrate --config drizzle/config.pg.ts",
66
62
  "prepublishOnly": "bun run build"
67
63
  },
68
64
  "publishConfig": {