-
Notifications
You must be signed in to change notification settings - Fork 126
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/lix-integration' into paraglide-…
…prerelease
- Loading branch information
Showing
49 changed files
with
1,176 additions
and
250 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { sql, type Kysely } from "kysely"; | ||
import type { SqliteDatabase } from "sqlite-wasm-kysely"; | ||
|
||
export async function createSchema(args: { | ||
db: Kysely<any>; | ||
sqlite: SqliteDatabase; | ||
}) { | ||
return sql` | ||
CREATE TABLE bundle ( | ||
id TEXT PRIMARY KEY DEFAULT (bundle_id()), | ||
alias TEXT NOT NULL | ||
); | ||
CREATE TABLE message ( | ||
id TEXT PRIMARY KEY DEFAULT (uuid_v4()), | ||
bundle_id TEXT NOT NULL, | ||
locale TEXT NOT NULL, | ||
declarations TEXT NOT NULL, | ||
selectors TEXT NOT NULL | ||
); | ||
CREATE TABLE variant ( | ||
id TEXT PRIMARY KEY DEFAULT (uuid_v4()), | ||
message_id TEXT NOT NULL, | ||
match TEXT NOT NULL, | ||
pattern TEXT NOT NULL | ||
); | ||
CREATE INDEX idx_message_bundle_id ON message (bundle_id); | ||
CREATE INDEX idx_variant_message_id ON variant (message_id); | ||
`.execute(args.db); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { createInMemoryDatabase } from "sqlite-wasm-kysely"; | ||
import { test, expect } from "vitest"; | ||
import { initDb } from "./initDb.js"; | ||
import { isBundleId } from "../bundle-id/bundle-id.js"; | ||
import { validate } from "uuid"; | ||
import { createSchema } from "./createSchema.js"; | ||
|
||
test("bundle ids should have a default value", async () => { | ||
const sqlite = await createInMemoryDatabase({ | ||
readOnly: false, | ||
}); | ||
const db = initDb({ sqlite }); | ||
await createSchema({ db, sqlite }); | ||
|
||
const bundle = await db | ||
.insertInto("bundle") | ||
.values({ | ||
alias: { | ||
mock: "mock", | ||
}, | ||
}) | ||
.returningAll() | ||
.executeTakeFirstOrThrow(); | ||
|
||
expect(isBundleId(bundle.id)).toBe(true); | ||
}); | ||
|
||
test("message ids should default to uuid", async () => { | ||
const sqlite = await createInMemoryDatabase({ | ||
readOnly: false, | ||
}); | ||
const db = initDb({ sqlite }); | ||
await createSchema({ db, sqlite }); | ||
|
||
const message = await db | ||
.insertInto("message") | ||
.values({ | ||
bundleId: "mock", | ||
locale: "en", | ||
selectors: [], | ||
declarations: [], | ||
}) | ||
.returningAll() | ||
.executeTakeFirstOrThrow(); | ||
|
||
expect(validate(message.id)).toBe(true); | ||
}); | ||
|
||
test("variant ids should default to uuid", async () => { | ||
const sqlite = await createInMemoryDatabase({ | ||
readOnly: false, | ||
}); | ||
const db = initDb({ sqlite }); | ||
await createSchema({ db, sqlite }); | ||
|
||
const variant = await db | ||
.insertInto("variant") | ||
.values({ | ||
messageId: "mock", | ||
match: {}, | ||
pattern: [], | ||
}) | ||
.returningAll() | ||
.executeTakeFirstOrThrow(); | ||
|
||
expect(validate(variant.id)).toBe(true); | ||
}); | ||
|
||
test("it should handle json serialization", async () => { | ||
const sqlite = await createInMemoryDatabase({ | ||
readOnly: false, | ||
}); | ||
const db = initDb({ sqlite }); | ||
await createSchema({ db, sqlite }); | ||
|
||
const bundle = await db | ||
.insertInto("bundle") | ||
.values({ | ||
alias: { mock: "mock" }, | ||
}) | ||
.returningAll() | ||
.executeTakeFirstOrThrow(); | ||
|
||
expect(bundle.alias).toEqual({ mock: "mock" }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { CamelCasePlugin, Kysely, ParseJSONResultsPlugin } from "kysely"; | ||
import type { InlangDatabaseSchema } from "./schema.js"; | ||
import { createDialect, type SqliteDatabase } from "sqlite-wasm-kysely"; | ||
import { v4 } from "uuid"; | ||
import { generateBundleId } from "../bundle-id/bundle-id.js"; | ||
import { SerializeJsonPlugin } from "./serializeJsonPlugin.js"; | ||
|
||
export function initDb(args: { sqlite: SqliteDatabase }) { | ||
initDefaultValueFunctions({ sqlite: args.sqlite }); | ||
const db = new Kysely<InlangDatabaseSchema>({ | ||
dialect: createDialect({ | ||
database: args.sqlite, | ||
}), | ||
plugins: [ | ||
new ParseJSONResultsPlugin(), | ||
new CamelCasePlugin(), | ||
new SerializeJsonPlugin(), | ||
], | ||
}); | ||
return db; | ||
} | ||
|
||
function initDefaultValueFunctions(args: { sqlite: SqliteDatabase }) { | ||
args.sqlite.createFunction({ | ||
name: "uuid_v4", | ||
arity: 0, | ||
xFunc: () => v4(), | ||
}); | ||
args.sqlite.createFunction({ | ||
name: "bundle_id", | ||
arity: 0, | ||
xFunc: () => generateBundleId(), | ||
}); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
import type { Selectable } from "kysely"; | ||
import type { Bundle, Message, Variant } from "../schema/schemaV2.js"; | ||
import type { InlangDatabaseSchema } from "./schema.js"; | ||
|
||
let _; | ||
|
||
// expect a bundle to equal the type that the database returns | ||
_ = {} as Bundle satisfies Selectable<InlangDatabaseSchema["bundle"]>; | ||
|
||
// expect a message to equal the type that the database returns | ||
_ = {} as Message satisfies Selectable<InlangDatabaseSchema["message"]>; | ||
|
||
// expect a variant to equal the type that the database returns | ||
_ = {} as Variant satisfies Selectable<InlangDatabaseSchema["variant"]>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,49 @@ | ||
import type { Generated, Insertable, Selectable, Updateable } from "kysely"; | ||
import type { Bundle, Message, Variant } from "../schema/schemaV2.js"; | ||
|
||
export type InlangDatabaseSchema = { | ||
bundle: Bundle; | ||
message: Message; | ||
variant: Variant; | ||
bundle: BundleTable; | ||
message: MessageTable; | ||
variant: VariantTable; | ||
}; | ||
|
||
type BundleTable = Omit<Bundle, "id"> & { | ||
id: Generated<string>; | ||
}; | ||
|
||
type MessageTable = Omit<Message, "id"> & { | ||
id: Generated<string>; | ||
}; | ||
|
||
type VariantTable = Omit<Variant, "id"> & { | ||
id: Generated<string>; | ||
}; | ||
|
||
export type NewBundle = Insertable<BundleTable>; | ||
export type BundleUpdate = Updateable<BundleTable>; | ||
|
||
export type NewMessage = Insertable<MessageTable>; | ||
export type MessageUpdate = Updateable<MessageTable>; | ||
|
||
export type NewVariant = Selectable<VariantTable>; | ||
export type VariantUpdate = Updateable<VariantTable>; | ||
|
||
export type MessageNested = Message & { | ||
variants: Variant[]; | ||
}; | ||
export type NewMessageNested = NewMessage & { | ||
variants: NewVariant[]; | ||
}; | ||
export type MessageNestedUpdate = Updateable<MessageTable> & { | ||
variants: VariantUpdate[]; | ||
}; | ||
|
||
export type BundleNested = Bundle & { | ||
messages: MessageNested[]; | ||
}; | ||
export type NewBundleNested = NewBundle & { | ||
messages: NewMessageNested[]; | ||
}; | ||
export type BundleNestedUpdate = BundleUpdate & { | ||
messages: MessageNestedUpdate[]; | ||
}; |
90 changes: 90 additions & 0 deletions
90
inlang/source-code/sdk2/src/database/serializeJsonPlugin.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { | ||
OperationNodeTransformer, | ||
sql, | ||
ValueListNode, | ||
ValueNode, | ||
ValuesNode, | ||
type KyselyPlugin, | ||
type PluginTransformQueryArgs, | ||
type PluginTransformResultArgs, | ||
type QueryResult, | ||
type RootOperationNode, | ||
type UnknownRow, | ||
} from "kysely"; | ||
|
||
export class SerializeJsonPlugin implements KyselyPlugin { | ||
#parseJsonTransformer = new ParseJsonTransformer(); | ||
|
||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode { | ||
if ( | ||
args.node.kind === "InsertQueryNode" || | ||
args.node.kind === "UpdateQueryNode" | ||
) { | ||
const result = this.#parseJsonTransformer.transformNode(args.node); | ||
|
||
return result; | ||
} | ||
return args.node; | ||
} | ||
|
||
async transformResult( | ||
args: PluginTransformResultArgs | ||
): Promise<QueryResult<UnknownRow>> { | ||
return args.result; | ||
} | ||
} | ||
|
||
class ParseJsonTransformer extends OperationNodeTransformer { | ||
protected override transformValueList(node: ValueListNode): ValueListNode { | ||
return super.transformValueList({ | ||
...node, | ||
values: node.values.map((listNodeItem) => { | ||
if (listNodeItem.kind !== "ValueNode") { | ||
return listNodeItem; | ||
} | ||
|
||
// @ts-ignore | ||
const { value } = listNodeItem; | ||
|
||
const serializedValue = serializeJson(value); | ||
|
||
if (value === serializedValue) { | ||
return listNodeItem; | ||
} | ||
|
||
// TODO use jsonb. depends on parsing again | ||
// https://github.com/opral/inlang-sdk/issues/132 | ||
return sql`json(${serializedValue})`.toOperationNode(); | ||
}), | ||
}); | ||
} | ||
|
||
override transformValues(node: ValuesNode): ValuesNode { | ||
return super.transformValues({ | ||
...node, | ||
values: node.values.map((valueItemNode) => { | ||
if (valueItemNode.kind !== "PrimitiveValueListNode") { | ||
return valueItemNode; | ||
} | ||
|
||
return { | ||
kind: "ValueListNode", | ||
values: valueItemNode.values.map( | ||
(value) => | ||
({ | ||
kind: "ValueNode", | ||
value, | ||
} as ValueNode) | ||
), | ||
} as ValueListNode; | ||
}), | ||
}); | ||
} | ||
} | ||
|
||
function serializeJson(value: any): string { | ||
if (typeof value === "object" || Array.isArray(value)) { | ||
return JSON.stringify(value); | ||
} | ||
return value; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.