diff --git a/CREATING_A_MODULE.md b/CREATING_A_MODULE.md
new file mode 100644
index 0000000..9b5f1a8
--- /dev/null
+++ b/CREATING_A_MODULE.md
@@ -0,0 +1,53 @@
+Certainly! Let's create a README document to guide users through the process of creating a module in BNK (Bun Nook Kit). This document will be structured to be user-friendly, informative, and aligned with BNK's principles and coding style.
+
+---
+
+# Creating a Module in BNK (Bun Nook Kit)
+
+## Overview
+This guide provides step-by-step instructions on how to create a new module for the BNK framework. Whether you're adding functionality like OAuth integration or something entirely different, these guidelines will help align the module with BNK's design philosophy and standards to ensure consistency.
+
+## Prerequisites
+- Familiarity with TypeScript and BNK's core concepts.
+- Understanding of the problem domain the module will address.
+
+## Step 1: Research and Requirements Gathering
+Before coding, understand the scope and requirements of the module. For instance, if you're building an OAuth module, research the OAuth 2.0 protocol, and identify the primary use cases you want to support.
+
+## Step 2: Designing the Module
+### 2.1 Define the API
+Design a clear and intuitive API for the module. Consider the functions and interfaces users will interact with.
+
+### 2.2 Plan the Architecture
+Ensure the module aligns with BNK's architecture. Use factory functions, avoid global state, and adhere to strong typing.
+
+### 2.3 Security and Performance
+Plan for security and performance from the start. This is especially important for modules handling sensitive data or requiring high efficiency.
+
+## Step 3: Implementation
+### 3.1 Setup
+Set up the basic structure of the module. Create a new directory and files as needed within the BNK project structure.
+
+### 3.2 Core Functionality
+Develop the core functionality of the module. Keep functions short and focused, and use descriptive names.
+
+### 3.3 Integration
+Ensure that the module integrates seamlessly with other BNK components.
+
+### 3.4 Error Handling
+Implement robust error handling to make the module resilient and reliable.
+
+## Step 4: Testing
+Write comprehensive tests for the module. Cover unit testing for individual functions and integration testing for the module as a whole.
+
+## Step 5: Documentation
+Document the module thoroughly. Include a usage guide, example implementations, and a detailed API reference.
+
+## Step 6: Community Feedback and Iteration
+Release a beta version of the module and encourage feedback from the BNK community. Iterate based on the feedback received.
+
+## Best Practices
+- **Follow BNK's Coding Style**: Adhere to the principles outlined in BNK's coding guidelines, such as using `const`, writing pure functions, and avoiding premature optimization.
+- **Use Descriptive Names**: Choose clear and descriptive names for functions, variables, and modules.
+- **Write Efficient Code**: Focus on practicality and optimization where necessary. Prioritize clarity and simplicity.
+
diff --git a/README.md b/README.md
index b14dabc..292d492 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,6 @@
Bun Nook Kit (BNK) is a comprehensive toolkit for software development, leveraging the power of Bun and TypeScript. With zero third-party dependencies, strong TypeScript inferencing, and a focus on Web API standards, BNK offers a modular, type-safe, and efficient way to build robust applications.
-## [View Modules Docs Site](https://nookit.dev/readme)
-#### [Doc Repo](https://github.com/brandon-schabel/bun-nook-kit-docs)
## Quick Start
@@ -34,9 +32,10 @@ bun dev
Visit `http://localhost:3000` in your browser and you should see Hello world and
`http://localhost:3000/json` for the json
-## Usage Overview
+## [View Modules Docs Site](https://nookit.dev/readme)
+### [Doc Repo](https://github.com/brandon-schabel/bun-nook-kit-docs)
-### Install
+## Bun Nook Kit Installation
```bash
bun add bnkit
@@ -72,7 +71,7 @@ start()
## Discord Server
-Join our [Discord Server]("https://discord.gg/rQyWN7V6"), drop in and ask questions, give feedback or just for a chat!
+Join our [Discord Server]("https://discord.gg/rQyWN7V6") https://discord.gg/rQyWN7V6, drop in and ask questions, give feedback or just for a chat!
## Key Highlights
@@ -82,7 +81,7 @@ Join our [Discord Server]("https://discord.gg/rQyWN7V6"), drop in and ask questi
- **TypeSafe with Strong TypeScript type Inferencing** - Strong types tell you where things are incorrect, strong type inferrence allows you to utilize the advantages of strong types and not having to deal with too much TypeScript.
-- **Modular** Everything is built with as little dependency other other modules in the repo, however they still work together. Soon I'll be working on a full stack auth package which will utilize everything from server routes, cookies, database(sqlite).
+- **Modular** Everything is built with as little direct dependency on other modules in the repo, however they still work together. Soon I'll be working on a full stack auth package which will utilize everything from server routes, cookies, database(sqlite).
- **Builds on Web APIs** Bun itself is built on strong principles of sticking to Web APIs In order to maintain as much comptaibility across various packages, BNK sticks to the fundementals of the webplatform APIs.
diff --git a/auth/example/.env.example b/auth/example/.env.example
new file mode 100644
index 0000000..5cc4709
--- /dev/null
+++ b/auth/example/.env.example
@@ -0,0 +1,3 @@
+# rename this to .env anf ollow "Setup google oauth" instructions
+GOOGLE_OAUTH_CLIENT_ID=""
+GOOGLE_OAUTH_CLIENT_SECRET=""
\ No newline at end of file
diff --git a/auth/example/google-oauth-server-example.ts b/auth/example/google-oauth-server-example.ts
new file mode 100644
index 0000000..5fe0dc9
--- /dev/null
+++ b/auth/example/google-oauth-server-example.ts
@@ -0,0 +1,69 @@
+import { oAuthFactory } from "auth/oauth";
+import { initGoogleOAuth } from "auth/oauth-providers";
+import type { Routes } from "server";
+import { serverFactory } from "server";
+
+const googleClientId = Bun.env.GOOGLE_OAUTH_CLIENT_ID || "";
+const googleClientSecret = Bun.env.GOOGLE_OAUTH_CLIENT_SECRET || "";
+
+
+const googleOAuthConfig = initGoogleOAuth({
+ clientId: googleClientId,
+ clientSecret: googleClientSecret,
+});
+
+const googleOAuth = oAuthFactory(googleOAuthConfig);
+
+const routes = {
+ "/login": {
+ GET: () => {
+ // you could pass a param for the provider
+ const authUrl = googleOAuth.initiateOAuthFlow();
+
+ return new Response(null, {
+ headers: { Location: authUrl },
+ status: 302,
+ });
+ },
+ },
+ "/callback": {
+ GET: async (req) => {
+ try {
+ const host = req.headers.get("host");
+ // Parse the URL and query parameters
+ const url = new URL(req.url, `http://${host}`);
+ const queryParams = new URLSearchParams(url.search);
+ const code = queryParams.get("code");
+
+ if (!code) {
+ return new Response("No code provided in query", { status: 400 });
+ }
+
+ const tokenInfo = await googleOAuth.handleRedirect(code);
+
+ console.log({ tokenInfo });
+
+ // Logic after successful authentication
+ return new Response("Login Successful!");
+ } catch (error) {
+ console.error(error);
+ return new Response("Authentication failed", { status: 403 });
+ }
+ },
+ },
+ "/": {
+ GET: () => {
+ // HTML content for the login page
+ const htmlContent = `
Login with Google
`;
+ return new Response(htmlContent, {
+ headers: { "Content-Type": "text/html" },
+ });
+ },
+ },
+} satisfies Routes;
+
+const server = serverFactory({
+ routes,
+});
+
+server.start(3000);
diff --git a/auth/example/setup-oauth-providers.md b/auth/example/setup-oauth-providers.md
new file mode 100644
index 0000000..85898d3
--- /dev/null
+++ b/auth/example/setup-oauth-providers.md
@@ -0,0 +1,81 @@
+# OAuth Setup Instructions
+
+## Google OAuth Setup
+
+#### Step 1: Create a Google Cloud Project
+- **Access Google Cloud Console**: Go to [Google Cloud Console](https://console.cloud.google.com/).
+- **New Project**: Click 'New Project', name it, and create.
+
+#### Step 2: Configure OAuth Consent Screen
+- **Credentials Page**: Navigate to 'Credentials' under 'APIs & Services'.
+- **Consent Screen Setup**: Click 'Configure Consent Screen', select 'External', and create.
+- **Details**: Enter app name, support email, and developer email. Add optional details like logo and policy links.
+- **Save**: Click 'Save and Continue'.
+
+#### Step 3: Create OAuth 2.0 Credentials
+- **Credentials Creation**: Back on 'Credentials' page, select 'Create Credentials' > 'OAuth client ID'.
+- **Application Type**: Choose 'Web application'.
+- **Redirect URIs**: Add your redirect URI (/callback).
+- **Client ID & Secret**: After clicking 'Create', note down the client ID and secret.
+
+#### Step 4: Enable Required APIs
+- **API Library**: In 'Library', search and enable needed Google APIs.
+
+#### Step 5: Implement OAuth in Your App
+- **Integrate Credentials**: Use client ID and secret in your app's OAuth config.
+- **Handle Redirects**: Ensure handling of Google's redirects and token exchange.
+
+#### Step 6: Test and Deploy
+- **Testing**: Thoroughly test the OAuth flow.
+- **Verification and Deployment**: Submit for verification if needed and deploy.
+
+This guide provides a condensed overview of setting up Google OAuth. Adapt it based on your specific application needs and always prioritize security and privacy.
+
+## GitHub OAuth
+
+1. **Register Your Application**: Go to GitHub's Developer Settings and create a new OAuth application.
+2. **Client ID & Secret**: After registration, you'll receive a client ID and client secret.
+3. **Authorization URL**: Use `https://github.com/login/oauth/authorize` for user authorization.
+4. **Token URL**: Use `https://github.com/login/oauth/access_token` to exchange the code for a token.
+5. **Scopes**: Decide on the scopes you need, like `user:email` for email access.
+6. **Callback URL**: Set your callback URL that GitHub will redirect to after authentication.
+
+
+#### Don't Forget:
+
+1. **Submit for Verification**: If your application will be used by users outside your organization, you must submit your OAuth consent screen for verification by Google.
+
+
+## Meta (Facebook) OAuth
+
+1. **Facebook App**: Create a new app in the Facebook Developer portal.
+2. **Client ID & Secret**: Obtain these from your app's settings.
+3. **Authorization URL**: Use `https://www.facebook.com/v9.0/dialog/oauth`.
+4. **Token URL**: Use `https://graph.facebook.com/v9.0/oauth/access_token`.
+5. **Scopes**: Define scopes, such as `email` and `public_profile`.
+6. **Callback URL**: Set your redirect URI in the app settings.
+
+## Twitter OAuth
+
+1. **Create Twitter App**: Register your app on Twitter Developer Portal.
+2. **Keys and Tokens**: You'll get API key, API secret key, Access token, and Access token secret.
+3. **Authorization URL**: Use `https://api.twitter.com/oauth/authorize`.
+4. **Token URL**: Use `https://api.twitter.com/oauth/access_token`.
+5. **Callback URL**: Define your callback URL in the Twitter app settings.
+
+## Apple OAuth
+
+1. **Register with Apple Developer**: Create an app in Apple Developer portal.
+2. **Service ID & Secret**: Generate a Services ID and a secret key.
+3. **Authorization URL**: Use `https://appleid.apple.com/auth/authorize`.
+4. **Token URL**: Use `https://appleid.apple.com/auth/token`.
+5. **Scopes**: Define necessary scopes like `email` and `name`.
+6. **Redirect URIs**: Add your redirect URIs in the Apple Developer console.
+
+### Implementation Steps:
+
+1. **Setup OAuth Provider Configuration**: For each service, configure the details in your OAuth setup, similar to how you did with Google.
+2. **Redirect to Authorization URL**: On your login page, add buttons for each service that redirects to their respective authorization URL with the necessary query parameters.
+3. **Handle Callbacks**: Implement routes in your server to handle the callbacks, exchanging the authorization code for tokens.
+4. **User Authentication**: Use the tokens to fetch user details and authenticate or register them in your system.
+
diff --git a/auth/index.ts b/auth/index.ts
index e3edcab..626a2e0 100644
--- a/auth/index.ts
+++ b/auth/index.ts
@@ -2,5 +2,9 @@ export {
createSecurityToken,
createToken,
getTokenExpireEpoch,
- verifyToken,
+ verifyToken
} from "./security-token";
+
+export { oAuthFactory } from "./oauth";
+
+export { initGoogleOAuth, oAuthProviders } from "./oauth-providers";
diff --git a/auth/oauth-providers.ts b/auth/oauth-providers.ts
new file mode 100644
index 0000000..cd308b9
--- /dev/null
+++ b/auth/oauth-providers.ts
@@ -0,0 +1,33 @@
+import { OAuthConfig, OAuthProviderFn } from "./oauth-types";
+
+export type ProvidersConfigRecord = Record<
+ string,
+ Omit
+>;
+
+export const oAuthProviders = {
+ google: {
+ redirectUri: "http://localhost:3000/callback", // just a default placeholder
+ authReqUrl: "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenUrl: "https://oauth2.googleapis.com/token",
+ },
+ microsoft: {
+ redirectUri: "http://localhost:3000/callback",
+ authReqUrl:
+ "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
+ tokenUrl: "http://needtofind",
+ },
+} satisfies ProvidersConfigRecord;
+
+export const initGoogleOAuth: OAuthProviderFn = (
+ { clientId, clientSecret },
+ options
+) => {
+ const redirectUrl = options?.redirectUrl;
+ return {
+ ...oAuthProviders.google,
+ redirectUri: redirectUrl ? redirectUrl : oAuthProviders.google.redirectUri,
+ clientId,
+ clientSecret,
+ };
+};
diff --git a/auth/oauth-types.ts b/auth/oauth-types.ts
new file mode 100644
index 0000000..2d56e52
--- /dev/null
+++ b/auth/oauth-types.ts
@@ -0,0 +1,36 @@
+export type OAuthHelpers = {
+ getAuthorizationUrl(config: OAuthConfig): string;
+ getToken(code: string, config: OAuthConfig): Promise; // Simplified for demonstration
+};
+
+export type OAuthConfig = {
+ clientId: string;
+ clientSecret: string;
+ // the server route that handles the redirect from the OAuth provider
+ redirectUri: string;
+ // the url that handles the token request from the OAuth provider
+ tokenUrl: string;
+ // the server route that handles the token request from the OAuth provider
+ authReqUrl: string;
+};
+
+export type OAuthToken = {
+ accessToken: string;
+ tokenType: string;
+ expiresIn: number; // Time in seconds after which the token expires
+ refreshToken?: string; // Optional, not all flows return a refresh token
+ scope?: string; // Optional, scope of the access granted
+ idToken?: string; // Optional, used in OpenID Connect (OIDC)
+ // Additional fields can be added here depending on the OAuth provider
+};
+
+export type OAuthProviderOptions = {
+ redirectUrl: string;
+};
+
+export type OAuthProviderCreds = Pick;
+export type OAuthProviderFn = (
+ config: OAuthProviderCreds,
+ options?: OAuthProviderOptions
+) => OAuthConfig;
+export type OAuthProviderInitializer = (config: OAuthConfig) => OAuthHelpers;
diff --git a/auth/oauth.ts b/auth/oauth.ts
new file mode 100644
index 0000000..0a1f9c1
--- /dev/null
+++ b/auth/oauth.ts
@@ -0,0 +1,89 @@
+import {
+ OAuthConfig,
+ OAuthProviderInitializer,
+ OAuthToken,
+} from "./oauth-types";
+
+type FetcherResponse = T & {
+ error?: string;
+};
+
+// Generic OAuth fetcher
+export async function oAuthFetcher(
+ url: string,
+ params: Record
+): Promise> {
+ const response = await fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ body: new URLSearchParams(params).toString(),
+ });
+
+ return response.json();
+}
+
+// Wrapper function for getting token
+export async function getOAuthToken({
+ code,
+ config: { clientId, clientSecret, redirectUri, tokenUrl },
+}: {
+ code: string;
+ config: Omit;
+}): Promise> {
+ const params = {
+ code,
+ client_id: clientId,
+ client_secret: clientSecret,
+ redirect_uri: redirectUri,
+ grant_type: "authorization_code",
+ };
+
+ return oAuthFetcher(tokenUrl, params);
+}
+
+export const initProvider: OAuthProviderInitializer = ({
+ clientId,
+ authReqUrl,
+ redirectUri,
+}) => {
+ return {
+ // TODO add options to be able to change response_type/scope, etc
+ getAuthorizationUrl: () => {
+ const authUrl = authReqUrl;
+ const queryParams = new URLSearchParams({
+ client_id: clientId,
+ redirect_uri: redirectUri,
+ response_type: "code",
+ scope: "email profile",
+ });
+
+ return `${authUrl}?${queryParams.toString()}`;
+ },
+ getToken: async (code: string) => {
+ return getOAuthToken({ code, config }).then((response) => {
+ if (response.error) {
+ console.error("Error fetching token:", response.error);
+ throw new Error(response.error);
+ } else {
+ console.log("Access Token:", response.accessToken);
+ return response;
+ }
+ });
+ },
+ };
+};
+
+export const oAuthFactory = (config: OAuthConfig) => {
+ const provider = initProvider(config);
+
+ return {
+ handleRedirect: async (code: string) => {
+ return await provider.getToken(code, config);
+ },
+ initiateOAuthFlow: () => {
+ return provider.getAuthorizationUrl(config);
+ },
+ };
+};
diff --git a/data-gen/object-gen.test.ts b/data-gen/object-gen.test.ts
index 48e3067..9104f81 100644
--- a/data-gen/object-gen.test.ts
+++ b/data-gen/object-gen.test.ts
@@ -87,4 +87,3 @@ describe("inferTypeAndGenerate", () => {
}
});
});
-
diff --git a/data-gen/object-gen.ts b/data-gen/object-gen.ts
index 44a8248..d11ee35 100644
--- a/data-gen/object-gen.ts
+++ b/data-gen/object-gen.ts
@@ -7,11 +7,11 @@ interface DataGenerators {
date: (generator?: DataGenerator) => Date;
object: >(
shape: T,
- generatorMap?: Partial>>
+ generatorMap?: Partial>>,
) => DataGenerator;
array: (
generator: DataGenerator,
- length?: number
+ length?: number,
) => DataGenerator;
}
@@ -22,7 +22,8 @@ export const dataGenerators: DataGenerators = {
date: (generator = () => new Date()) => generator(),
object: >(
shape: T,
- generatorMap: Partial>> = {})=>{
+ generatorMap: Partial>> = {},
+ ) => {
return () => {
const result = {} as any;
for (const key in shape) {
@@ -36,7 +37,7 @@ export const dataGenerators: DataGenerators = {
return result as T;
};
},
- array: (generator: DataGenerator, length = 10) => {
+ array: (generator: DataGenerator, length = 10) => {
return () => {
const result = [];
for (let i = 0; i < length; i++) {
@@ -47,7 +48,7 @@ export const dataGenerators: DataGenerators = {
},
};
-export const inferTypeAndGenerate = (value: Val): any => {
+export const inferTypeAndGenerate = (value: Val): any => {
switch (typeof value) {
case "string":
return dataGenerators.string();
@@ -64,7 +65,7 @@ export const inferTypeAndGenerate = (value: Val): any => {
const inferredTypeGenerator = inferTypeAndGenerate(value[0]);
return dataGenerators.array(
() => inferredTypeGenerator,
- value.length
+ value.length,
)();
} else if (typeof value === "object" && value !== null) {
return dataGenerators.object(value as Record)();
diff --git a/htmlody/css-engine.ts b/htmlody/css-engine.ts
index 7011d5f..dd96157 100644
--- a/htmlody/css-engine.ts
+++ b/htmlody/css-engine.ts
@@ -846,6 +846,15 @@ export const CSS_MAP = {
"grid-cols-2": "grid-template-columns: repeat(2, minmax(0, 1fr));",
"grid-cols-3": "grid-template-columns: repeat(3, minmax(0, 1fr));",
"grid-cols-4": "grid-template-columns: repeat(4, minmax(0, 1fr));",
+ "grid-cols-5": "grid-template-columns: repeat(5, minmax(0, 1fr));",
+ "grid-cols-6": "grid-template-columns: repeat(6, minmax(0, 1fr));",
+ "grid-cols-7": "grid-template-columns: repeat(7, minmax(0, 1fr));",
+ "grid-cols-8": "grid-template-columns: repeat(8, minmax(0, 1fr));",
+ "grid-cols-9": "grid-template-columns: repeat(9, minmax(0, 1fr));",
+ "grid-cols-10": "grid-template-columns: repeat(10, minmax(0, 1fr));",
+ "grid-cols-11": "grid-template-columns: repeat(11, minmax(0, 1fr));",
+ "grid-cols-12": "grid-template-columns: repeat(12, minmax(0, 1fr));",
+
// Text Utilities
"text-left": textAlign("left"),
diff --git a/htmlody/json-to-html-engine.ts b/htmlody/json-to-html-engine.ts
index e3e98fd..64a0801 100644
--- a/htmlody/json-to-html-engine.ts
+++ b/htmlody/json-to-html-engine.ts
@@ -57,7 +57,6 @@ export function renderHtmlTag({
validate?: boolean;
}): string {
if (validate) {
- console.log("validating tag name", tagName)
validateTagName(tagName);
}
@@ -410,7 +409,7 @@ export const htmlodyBuilder = <
const html = buildHtmlDoc(bodyConfig, options);
return new Response(html, {
headers: {
- "Content-Type": "text/html",
+ "Content-Type": "text/html; charset=utf-8",
},
});
};
diff --git a/release.ts b/release.ts
index 0718af9..323a2bc 100644
--- a/release.ts
+++ b/release.ts
@@ -1,13 +1,14 @@
import Bun from "bun";
import path from "path";
import { exit } from "process";
-import * as u from "./";
+// import * as u from "./";
import { ulog } from "./utils/ulog";
+import { deploy, npm } from "index";
// run bun test
const testProc = Bun.spawnSync(["bun", "test", "--coverage"], {});
-const output = await u.deploy.logStdOutput(testProc);
+const output = await deploy.logStdOutput(testProc);
if (!output) {
ulog("No output");
@@ -20,12 +21,12 @@ const MAX_RETRIES = Number(Bun.env.MAX_PUBLISH_RETRY) || 10; // Define a max num
const e = Bun.env;
const { npmPublish, setupNpmAuth, updatePackageVersion } =
- u.npm.npmReleaseFactory({
+ npm.npmReleaseFactory({
maxRetries: MAX_RETRIES,
npmToken: NPM_TOKEN,
});
-const { commitAndPush, setupGitConfig } = u.deploy.createGitHubActionsFactory({
+const { commitAndPush, setupGitConfig } = deploy.createGitHubActionsFactory({
sshRepoUrl: "git@github.com:brandon-schabel/bun-nook-kit.git",
});
diff --git a/sqlite/sqlite-table-factory.ts b/sqlite/sqlite-table-factory.ts
index 2c71840..b710075 100644
--- a/sqlite/sqlite-table-factory.ts
+++ b/sqlite/sqlite-table-factory.ts
@@ -3,6 +3,7 @@ import { SQLiteSchemaInfer, SchemaMap } from "./sqlite-factory";
import {
createItem,
deleteItemById,
+ readFirstItemByKey,
readItemById,
readItems,
readItemsWhere,
@@ -62,6 +63,15 @@ export function sqliteTableFactory<
readById(id: string | number) {
return readItemById(db, tableName, log, id);
},
+ readItemByKey(key: string, value: string | number) {
+ return readFirstItemByKey(
+ db,
+ tableName,
+ log,
+ key,
+ value
+ ) as unknown as TranslatedSchema;
+ },
update(id: string | number, item: Partial>) {
return updateItem(db, tableName, log, id, item);
},
diff --git a/sqlite/sqlite-utils/crud-fn-utils.ts b/sqlite/sqlite-utils/crud-fn-utils.ts
index 7c42125..b889fea 100644
--- a/sqlite/sqlite-utils/crud-fn-utils.ts
+++ b/sqlite/sqlite-utils/crud-fn-utils.ts
@@ -21,8 +21,12 @@ export function createItem<
const valuesArray = Object.values(item);
log({ query, valuesArray });
- // Perform the insert operation
- db.query(query).run(...valuesArray);
+ try {
+ // Perform the insert operation
+ db.query(query).run(...valuesArray);
+ } catch (e) {
+ throw e;
+ }
if (returnInsertedItem) {
// Assuming your db instance has a method to get the last inserted row id
@@ -30,16 +34,37 @@ export function createItem<
// Query to select the last inserted item
const selectQuery = `SELECT * FROM ${tableName} WHERE id = last_insert_rowid();`;
- const insertedItem = db.query(selectQuery).get() as TranslatedSchema;
- log({ selectQuery, lastId: insertedItem.id, insertedItem });
- return insertedItem as TranslatedSchema;
+ try {
+ const insertedItem = db.query(selectQuery).get() as TranslatedSchema;
+
+ log({ selectQuery, lastId: insertedItem.id, insertedItem });
+ return insertedItem as TranslatedSchema;
+ } catch (e) {
+ throw e;
+ }
}
// If not returning the inserted item, return an empty array
return null;
}
+export function readFirstItemByKey<
+ Schema extends SchemaMap,
+ TranslatedSchema extends SQLiteSchemaInfer = SQLiteSchemaInfer
+>(
+ db: Database,
+ tableName: string,
+ log: (msg: any) => void,
+ key: keyof TranslatedSchema,
+ value: string | number
+): TranslatedSchema {
+ const queryString = selectItemByKeyQueryString(tableName, String(key));
+ log(queryString);
+ const query = db.prepare(queryString).get(value) as TranslatedSchema;
+ return query;
+}
+
// Modify the readItems function to include an optional id parameter.
export function readItemById<
Schema extends SchemaMap,
@@ -50,10 +75,11 @@ export function readItemById<
log: (msg: any) => void,
id: string | number // Add an optional id parameter
): TranslatedSchema {
- const query = selectItemByIdQueryString(tableName, id);
+ const query = selectItemByKeyQueryString(tableName, "id");
log(query);
- // Use the ID in the parameterized query to prevent SQL injection.
- const data = db.query(query).get({ $id: id }) as TranslatedSchema;
+
+ const data = db.prepare(query).get(id) as TranslatedSchema;
+
return data;
}
@@ -104,11 +130,12 @@ export function readItemsWhere<
}
// In your crud-string-utils file, add a function to create a SQL query string to select by ID.
-export function selectItemByIdQueryString(
+export function selectItemByKeyQueryString(
tableName: string,
- id: string | number
+ key: string
+ // value: string | number
): string {
- return `SELECT * FROM ${tableName} WHERE id = $id;`;
+ return `SELECT * FROM ${tableName} WHERE ${key} = ?`;
}
export function readItems<