adding freight web and holding
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
import * as StarKitten from '@star-kitten/discord';
|
||||
import type { ExecutableInteraction } from '@star-kitten/discord';
|
||||
import { createActionRow, createButton, createContainer, createTextDisplay } from '@star-kitten/discord/components';
|
||||
import type { PageContext } from '@star-kitten/discord/pages';
|
||||
import { type Appraisal } from '@star-kitten/eve/third-party/janice.js';
|
||||
import { formatNumberToShortForm } from '@star-kitten/util/text.js';
|
||||
|
||||
export function renderAppraisal(
|
||||
appraisal: Appraisal,
|
||||
pageCtx: PageContext<any>,
|
||||
interaction: ExecutableInteraction,
|
||||
) {
|
||||
const formatter = new Intl.NumberFormat(interaction.locale || 'en-US', {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
const world = 'world';
|
||||
return (
|
||||
StarKitten.createElement("ActionRow", {}, StarKitten.createElement("Container", {"color":"0x1da57a"}, StarKitten.createElement("TextDisplay", {}, ""+ `Hello ${world}` +""), pageCtx.state.currentPage !== "share" ? StarKitten.createElement("ActionRow", {}, StarKitten.createElement("Button", {"key":"share","disabled":"{!unknown}"}, "Share in Channel")) : undefined))
|
||||
)
|
||||
}
|
||||
import * as StarKitten from '@star-kitten/discord';
|
||||
import type { ExecutableInteraction } from '@star-kitten/discord';
|
||||
import { createActionRow, createButton, createContainer, createTextDisplay } from '@star-kitten/discord/components';
|
||||
import type { PageContext } from '@star-kitten/discord/pages';
|
||||
import { type Appraisal } from '@star-kitten/eve/third-party/janice.js';
|
||||
import { formatNumberToShortForm } from '@star-kitten/util/text.js';
|
||||
|
||||
export function renderAppraisal(
|
||||
appraisal: Appraisal,
|
||||
pageCtx: PageContext<any>,
|
||||
interaction: ExecutableInteraction,
|
||||
) {
|
||||
const formatter = new Intl.NumberFormat(interaction.locale || 'en-US', {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
const world = 'world';
|
||||
return (
|
||||
StarKitten.createElement("ActionRow", {}, StarKitten.createElement("Container", {"color":"0x1da57a"}, StarKitten.createElement("TextDisplay", {}, ""+ `Hello ${world}` +""), pageCtx.state.currentPage !== "share" ? StarKitten.createElement("ActionRow", {}, StarKitten.createElement("Button", {"key":"share","disabled":"{!unknown}"}, "Share in Channel")) : undefined))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import type {} from '@star-kitten/discord/jsx';
|
||||
import { ActionRow, Container, Button, TextDisplay } from '@star-kitten/discord';
|
||||
|
||||
export function renderAppraisal() {
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
const world = 'world';
|
||||
const rand = Math.random() * 1000;
|
||||
const pageCtx = { state: { currentPage: 'home' } };
|
||||
|
||||
let jsx = (
|
||||
<ActionRow>
|
||||
<Container color="0x1da57a">
|
||||
<TextDisplay content={`Hello ${world}`} />
|
||||
{pageCtx.state.currentPage !== 'share' ?
|
||||
<ActionRow>
|
||||
<Button customId="share" label="Share in Channel" disabled={rand < 500} />
|
||||
</ActionRow>
|
||||
: undefined}
|
||||
</Container>
|
||||
</ActionRow>
|
||||
);
|
||||
|
||||
console.log(jsx);
|
||||
}
|
||||
|
||||
renderAppraisal();
|
||||
138
packages/discord/index.d.ts
vendored
138
packages/discord/index.d.ts
vendored
@@ -1,69 +1,69 @@
|
||||
import {
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
declare namespace JSX {
|
||||
type Component =
|
||||
| ActionRow
|
||||
| Button
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
| TextInput
|
||||
| LabelComponent
|
||||
| ContainerComponent
|
||||
| {
|
||||
type: 10;
|
||||
content: string;
|
||||
}
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent
|
||||
| InteractionButton
|
||||
| URLButton
|
||||
| PremiumButton
|
||||
| ThumbnailComponent;
|
||||
|
||||
type Element = Component | Promise<Component>;
|
||||
|
||||
interface ElementClass {
|
||||
render: any;
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props: {};
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// Allow any element, but prefer known elements
|
||||
[elemName: string]: any;
|
||||
// Known elements
|
||||
ActionRow: { children: any | any[] };
|
||||
Button: { label: string; customId: string; style?: number; emoji?: PartialEmoji; disabled?: boolean };
|
||||
Container: { accent?: number; spoiler?: boolean; children: any | any[] };
|
||||
TextDisplay: { content: string };
|
||||
}
|
||||
}
|
||||
import {
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
declare namespace JSX {
|
||||
type Component =
|
||||
| ActionRow
|
||||
| Button
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
| TextInput
|
||||
| LabelComponent
|
||||
| ContainerComponent
|
||||
| {
|
||||
type: 10;
|
||||
content: string;
|
||||
}
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent
|
||||
| InteractionButton
|
||||
| URLButton
|
||||
| PremiumButton
|
||||
| ThumbnailComponent;
|
||||
|
||||
type Element = Component | Promise<Component>;
|
||||
|
||||
interface ElementClass {
|
||||
render: any;
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props: {};
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// Allow any element, but prefer known elements
|
||||
[elemName: string]: any;
|
||||
// Known elements
|
||||
ActionRow: { children: any | any[] };
|
||||
Button: { label: string; customId: string; style?: number; emoji?: PartialEmoji; disabled?: boolean };
|
||||
Container: { accent?: number; spoiler?: boolean; children: any | any[] };
|
||||
TextDisplay: { content: string };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
},
|
||||
"author": "Author Name <author.name@mail.com>",
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -26,9 +27,15 @@
|
||||
"./pages": "./dist/pages/index.js",
|
||||
"./common": "./dist/common/index.js",
|
||||
"./package.json": "./package.json",
|
||||
"./jsx": "./src/jsx/jsx.ts",
|
||||
"./jsx-runtime": "./dist/jsx/index.js",
|
||||
"./jsx-dev-runtime": "./dist/jsx/index.js"
|
||||
"./jsx": "./dist/jsx/index.js",
|
||||
"./jsx-runtime": {
|
||||
"types": "./src/jsx/types.d.ts",
|
||||
"default": "./dist/jsx/jsx-runtime.js"
|
||||
},
|
||||
"./jsx-dev-runtime": {
|
||||
"types": "./src/jsx/types.d.ts",
|
||||
"default": "./dist/jsx/jsx-dev-runtime.js"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { Cache } from '@core/cache.type';
|
||||
import type { KVStore } from '@core/kv-store.type.ts';
|
||||
import type { Client } from '@projectdysnomia/dysnomia';
|
||||
import type { CommandState } from './command-state';
|
||||
|
||||
export interface PartialContext<T = any> {
|
||||
client: Client;
|
||||
cache: Cache;
|
||||
kv: KVStore;
|
||||
id?: string; // unique id for this command instance
|
||||
state?: CommandState<T>; // state associated with this command instance
|
||||
}
|
||||
|
||||
export type CommandContext<T = any> = Required<PartialContext<T>>;
|
||||
import type { Cache } from '@core/cache.type';
|
||||
import type { KVStore } from '@core/kv-store.type.ts';
|
||||
import type { Client } from '@projectdysnomia/dysnomia';
|
||||
import type { CommandState } from './command-state';
|
||||
|
||||
export interface PartialContext<T = any> {
|
||||
client: Client;
|
||||
cache: Cache;
|
||||
kv: KVStore;
|
||||
id?: string; // unique id for this command instance
|
||||
state?: CommandState<T>; // state associated with this command instance
|
||||
}
|
||||
|
||||
export type CommandContext<T = any> = Required<PartialContext<T>>;
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import {
|
||||
AutocompleteInteraction,
|
||||
CommandInteraction,
|
||||
ComponentInteraction,
|
||||
Constants,
|
||||
ModalSubmitInteraction,
|
||||
type ApplicationCommandOptionAutocomplete,
|
||||
type ApplicationCommandOptions,
|
||||
type ApplicationCommandStructure,
|
||||
type ChatInputApplicationCommandStructure,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
import type { CommandContext, PartialContext } from './command-context.type';
|
||||
|
||||
export interface CommandHandler<T extends ApplicationCommandStructure> {
|
||||
definition: T;
|
||||
execute: (interaction: ExecutableInteraction, ctx: CommandContext) => Promise<void>;
|
||||
}
|
||||
|
||||
export type ExecutableInteraction = CommandInteraction | ModalSubmitInteraction | ComponentInteraction | AutocompleteInteraction;
|
||||
|
||||
export type ChatCommandDefinition = Omit<ChatInputApplicationCommandStructure, 'type'>;
|
||||
export function createChatCommand(
|
||||
definition: ChatCommandDefinition,
|
||||
execute: (interaction: CommandInteraction, ctx: CommandContext) => Promise<void>,
|
||||
): CommandHandler<ChatInputApplicationCommandStructure> {
|
||||
const def = definition as ChatInputApplicationCommandStructure;
|
||||
def.type = 1; // CHAT_INPUT
|
||||
return {
|
||||
definition: def,
|
||||
execute,
|
||||
};
|
||||
}
|
||||
import {
|
||||
AutocompleteInteraction,
|
||||
CommandInteraction,
|
||||
ComponentInteraction,
|
||||
Constants,
|
||||
ModalSubmitInteraction,
|
||||
type ApplicationCommandOptionAutocomplete,
|
||||
type ApplicationCommandOptions,
|
||||
type ApplicationCommandStructure,
|
||||
type ChatInputApplicationCommandStructure,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
import type { CommandContext, PartialContext } from './command-context.type';
|
||||
|
||||
export interface CommandHandler<T extends ApplicationCommandStructure> {
|
||||
definition: T;
|
||||
execute: (interaction: ExecutableInteraction, ctx: CommandContext) => Promise<void>;
|
||||
}
|
||||
|
||||
export type ExecutableInteraction = CommandInteraction | ModalSubmitInteraction | ComponentInteraction | AutocompleteInteraction;
|
||||
|
||||
export type ChatCommandDefinition = Omit<ChatInputApplicationCommandStructure, 'type'>;
|
||||
export function createChatCommand(
|
||||
definition: ChatCommandDefinition,
|
||||
execute: (interaction: CommandInteraction, ctx: CommandContext) => Promise<void>,
|
||||
): CommandHandler<ChatInputApplicationCommandStructure> {
|
||||
const def = definition as ChatInputApplicationCommandStructure;
|
||||
def.type = 1; // CHAT_INPUT
|
||||
return {
|
||||
definition: def,
|
||||
execute,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import {
|
||||
Interaction,
|
||||
CommandInteraction,
|
||||
Constants,
|
||||
ModalSubmitInteraction,
|
||||
ComponentInteraction,
|
||||
AutocompleteInteraction,
|
||||
PingInteraction,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
import type { ExecutableInteraction } from './command-handler';
|
||||
|
||||
export function isApplicationCommand(interaction: Interaction): interaction is CommandInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.APPLICATION_COMMAND;
|
||||
}
|
||||
|
||||
export function isModalSubmit(interaction: Interaction): interaction is ModalSubmitInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.MODAL_SUBMIT;
|
||||
}
|
||||
|
||||
export function isMessageComponent(interaction: Interaction): interaction is ComponentInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.MESSAGE_COMPONENT;
|
||||
}
|
||||
|
||||
export function isAutocomplete(interaction: Interaction): interaction is AutocompleteInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.APPLICATION_COMMAND_AUTOCOMPLETE;
|
||||
}
|
||||
|
||||
export function isPing(interaction: Interaction): interaction is PingInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.PING;
|
||||
}
|
||||
|
||||
export function commandHasName(interaction: Interaction, name: string): boolean {
|
||||
return isApplicationCommand(interaction) && interaction.data.name === name;
|
||||
}
|
||||
|
||||
export function commandHasIdPrefix(interaction: Interaction, prefix: string): boolean {
|
||||
return (isModalSubmit(interaction) || isMessageComponent(interaction)) && interaction.data.custom_id.startsWith(prefix);
|
||||
}
|
||||
|
||||
export function getCommandName(interaction: ExecutableInteraction): string | undefined {
|
||||
if (isApplicationCommand(interaction) || isAutocomplete(interaction)) {
|
||||
return interaction.data.name;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
import {
|
||||
Interaction,
|
||||
CommandInteraction,
|
||||
Constants,
|
||||
ModalSubmitInteraction,
|
||||
ComponentInteraction,
|
||||
AutocompleteInteraction,
|
||||
PingInteraction,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
import type { ExecutableInteraction } from './command-handler';
|
||||
|
||||
export function isApplicationCommand(interaction: Interaction): interaction is CommandInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.APPLICATION_COMMAND;
|
||||
}
|
||||
|
||||
export function isModalSubmit(interaction: Interaction): interaction is ModalSubmitInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.MODAL_SUBMIT;
|
||||
}
|
||||
|
||||
export function isMessageComponent(interaction: Interaction): interaction is ComponentInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.MESSAGE_COMPONENT;
|
||||
}
|
||||
|
||||
export function isAutocomplete(interaction: Interaction): interaction is AutocompleteInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.APPLICATION_COMMAND_AUTOCOMPLETE;
|
||||
}
|
||||
|
||||
export function isPing(interaction: Interaction): interaction is PingInteraction {
|
||||
return interaction.type === Constants.InteractionTypes.PING;
|
||||
}
|
||||
|
||||
export function commandHasName(interaction: Interaction, name: string): boolean {
|
||||
return isApplicationCommand(interaction) && interaction.data.name === name;
|
||||
}
|
||||
|
||||
export function commandHasIdPrefix(interaction: Interaction, prefix: string): boolean {
|
||||
return (isModalSubmit(interaction) || isMessageComponent(interaction)) && interaction.data.custom_id.startsWith(prefix);
|
||||
}
|
||||
|
||||
export function getCommandName(interaction: ExecutableInteraction): string | undefined {
|
||||
if (isApplicationCommand(interaction) || isAutocomplete(interaction)) {
|
||||
return interaction.data.name;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
import { type InteractionModalContent, type Component } from '@projectdysnomia/dysnomia';
|
||||
import type { CommandContext, PartialContext } from './command-context.type';
|
||||
import { isApplicationCommand, isMessageComponent } from './command-helpers';
|
||||
import type { ExecutableInteraction } from './command-handler';
|
||||
|
||||
export function injectInteraction(interaction: ExecutableInteraction, ctx: PartialContext): [ExecutableInteraction, CommandContext] {
|
||||
// Wrap the interaction methods to inject command tracking ids into all custom_ids for modals and components.
|
||||
if (ctx.state.name && (isApplicationCommand(interaction) || isMessageComponent(interaction))) {
|
||||
const _originalCreateModal = interaction.createModal.bind(interaction);
|
||||
interaction.createModal = (content: InteractionModalContent) => {
|
||||
validateCustomIdLength(content.custom_id);
|
||||
content.custom_id = `${content.custom_id}_${ctx.state.id}`;
|
||||
return _originalCreateModal(content);
|
||||
};
|
||||
|
||||
const _originalCreateMessage = interaction.createMessage.bind(interaction);
|
||||
interaction.createMessage = (content) => {
|
||||
if (typeof content === 'string') return _originalCreateMessage(content);
|
||||
if (content.components) {
|
||||
addCommandIdToComponentCustomIds(content.components, ctx.state.id);
|
||||
}
|
||||
return _originalCreateMessage(content);
|
||||
};
|
||||
|
||||
const _originalEditMessage = interaction.editMessage.bind(interaction);
|
||||
interaction.editMessage = (messageID, content) => {
|
||||
if (typeof content === 'string') return _originalEditMessage(messageID, content);
|
||||
if (content.components) {
|
||||
addCommandIdToComponentCustomIds(content.components, ctx.state.id);
|
||||
}
|
||||
return _originalEditMessage(messageID, content);
|
||||
};
|
||||
|
||||
const _originalCreateFollowup = interaction.createFollowup.bind(interaction);
|
||||
interaction.createFollowup = (content) => {
|
||||
if (typeof content === 'string') return _originalCreateFollowup(content);
|
||||
if (content.components) {
|
||||
addCommandIdToComponentCustomIds(content.components, ctx.state.id);
|
||||
}
|
||||
return _originalCreateFollowup(content);
|
||||
};
|
||||
}
|
||||
return [interaction, ctx as CommandContext];
|
||||
}
|
||||
|
||||
function validateCustomIdLength(customId: string) {
|
||||
if (customId.length > 80) {
|
||||
throw new Error(`Custom ID too long: ${customId.length} characters (max 80) with this framework. Consider using shorter IDs.`);
|
||||
}
|
||||
}
|
||||
|
||||
function addCommandIdToComponentCustomIds(components: Component[], commandId: string) {
|
||||
components.forEach((component) => {
|
||||
if (!component) return;
|
||||
if ('custom_id' in component) {
|
||||
validateCustomIdLength(component.custom_id as string);
|
||||
component.custom_id = `${component.custom_id}_${commandId}`;
|
||||
}
|
||||
if ('components' in component && Array.isArray(component.components)) {
|
||||
addCommandIdToComponentCustomIds(component.components, commandId);
|
||||
}
|
||||
});
|
||||
}
|
||||
import { type InteractionModalContent, type Component } from '@projectdysnomia/dysnomia';
|
||||
import type { CommandContext, PartialContext } from './command-context.type';
|
||||
import { isApplicationCommand, isMessageComponent } from './command-helpers';
|
||||
import type { ExecutableInteraction } from './command-handler';
|
||||
|
||||
export function injectInteraction(interaction: ExecutableInteraction, ctx: PartialContext): [ExecutableInteraction, CommandContext] {
|
||||
// Wrap the interaction methods to inject command tracking ids into all custom_ids for modals and components.
|
||||
if (ctx.state.name && (isApplicationCommand(interaction) || isMessageComponent(interaction))) {
|
||||
const _originalCreateModal = interaction.createModal.bind(interaction);
|
||||
interaction.createModal = (content: InteractionModalContent) => {
|
||||
validateCustomIdLength(content.custom_id);
|
||||
content.custom_id = `${content.custom_id}_${ctx.state.id}`;
|
||||
return _originalCreateModal(content);
|
||||
};
|
||||
|
||||
const _originalCreateMessage = interaction.createMessage.bind(interaction);
|
||||
interaction.createMessage = (content) => {
|
||||
if (typeof content === 'string') return _originalCreateMessage(content);
|
||||
if (content.components) {
|
||||
addCommandIdToComponentCustomIds(content.components, ctx.state.id);
|
||||
}
|
||||
return _originalCreateMessage(content);
|
||||
};
|
||||
|
||||
const _originalEditMessage = interaction.editMessage.bind(interaction);
|
||||
interaction.editMessage = (messageID, content) => {
|
||||
if (typeof content === 'string') return _originalEditMessage(messageID, content);
|
||||
if (content.components) {
|
||||
addCommandIdToComponentCustomIds(content.components, ctx.state.id);
|
||||
}
|
||||
return _originalEditMessage(messageID, content);
|
||||
};
|
||||
|
||||
const _originalCreateFollowup = interaction.createFollowup.bind(interaction);
|
||||
interaction.createFollowup = (content) => {
|
||||
if (typeof content === 'string') return _originalCreateFollowup(content);
|
||||
if (content.components) {
|
||||
addCommandIdToComponentCustomIds(content.components, ctx.state.id);
|
||||
}
|
||||
return _originalCreateFollowup(content);
|
||||
};
|
||||
}
|
||||
return [interaction, ctx as CommandContext];
|
||||
}
|
||||
|
||||
function validateCustomIdLength(customId: string) {
|
||||
if (customId.length > 80) {
|
||||
throw new Error(`Custom ID too long: ${customId.length} characters (max 80) with this framework. Consider using shorter IDs.`);
|
||||
}
|
||||
}
|
||||
|
||||
function addCommandIdToComponentCustomIds(components: Component[], commandId: string) {
|
||||
components.forEach((component) => {
|
||||
if (!component) return;
|
||||
if ('custom_id' in component) {
|
||||
validateCustomIdLength(component.custom_id as string);
|
||||
component.custom_id = `${component.custom_id}_${commandId}`;
|
||||
}
|
||||
if ('components' in component && Array.isArray(component.components)) {
|
||||
addCommandIdToComponentCustomIds(component.components, commandId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
import { createReactiveState } from '@star-kitten/util/reactive-state.js';
|
||||
import type { PartialContext } from './command-context.type';
|
||||
import { isApplicationCommand, isAutocomplete } from './command-helpers';
|
||||
import type { ExecutableInteraction } from './command-handler';
|
||||
|
||||
export interface CommandState<T = any> {
|
||||
id: string; // unique id for this command instance
|
||||
name: string; // command name
|
||||
data: T; // internal data storage
|
||||
}
|
||||
|
||||
export async function getCommandState<T>(interaction: ExecutableInteraction, ctx: PartialContext): Promise<CommandState<T>> {
|
||||
const id = instanceIdFromInteraction(interaction);
|
||||
|
||||
let state: CommandState<T>;
|
||||
|
||||
// get state from kv store if possible
|
||||
if (ctx.kv.has(`command-state:${id}`)) {
|
||||
state = await ctx.kv.get<CommandState<T>>(`command-state:${id}`);
|
||||
}
|
||||
if (!state) {
|
||||
state = { id: id, name: '', data: {} as T };
|
||||
}
|
||||
const [reactiveState, subscribe] = createReactiveState(state);
|
||||
subscribe(async (newState) => {
|
||||
if (ctx.kv) {
|
||||
await ctx.kv.set(`command-state:${id}`, newState);
|
||||
}
|
||||
});
|
||||
ctx.state = reactiveState;
|
||||
return reactiveState;
|
||||
}
|
||||
|
||||
function instanceIdFromInteraction(interaction: ExecutableInteraction) {
|
||||
if (isAutocomplete(interaction)) {
|
||||
// autocomplete should not be stateful, they get no id
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isApplicationCommand(interaction)) {
|
||||
// for application commands, we create a new instance id
|
||||
const instance_id = crypto.randomUUID();
|
||||
return instance_id;
|
||||
}
|
||||
|
||||
const interact = interaction;
|
||||
const customId: string = interact.data.custom_id;
|
||||
const commandId = customId.split('_').pop();
|
||||
interaction;
|
||||
// command id should be a uuid
|
||||
if (commandId && /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(commandId)) {
|
||||
return commandId;
|
||||
}
|
||||
console.error(`Invalid command id extracted from interaction: ${customId}`);
|
||||
return '';
|
||||
}
|
||||
import { createReactiveState } from '@star-kitten/util/reactive-state.js';
|
||||
import type { PartialContext } from './command-context.type';
|
||||
import { isApplicationCommand, isAutocomplete } from './command-helpers';
|
||||
import type { ExecutableInteraction } from './command-handler';
|
||||
|
||||
export interface CommandState<T = any> {
|
||||
id: string; // unique id for this command instance
|
||||
name: string; // command name
|
||||
data: T; // internal data storage
|
||||
}
|
||||
|
||||
export async function getCommandState<T>(interaction: ExecutableInteraction, ctx: PartialContext): Promise<CommandState<T>> {
|
||||
const id = instanceIdFromInteraction(interaction);
|
||||
|
||||
let state: CommandState<T>;
|
||||
|
||||
// get state from kv store if possible
|
||||
if (ctx.kv.has(`command-state:${id}`)) {
|
||||
state = await ctx.kv.get<CommandState<T>>(`command-state:${id}`);
|
||||
}
|
||||
if (!state) {
|
||||
state = { id: id, name: '', data: {} as T };
|
||||
}
|
||||
const [reactiveState, subscribe] = createReactiveState(state);
|
||||
subscribe(async (newState) => {
|
||||
if (ctx.kv) {
|
||||
await ctx.kv.set(`command-state:${id}`, newState);
|
||||
}
|
||||
});
|
||||
ctx.state = reactiveState;
|
||||
return reactiveState;
|
||||
}
|
||||
|
||||
function instanceIdFromInteraction(interaction: ExecutableInteraction) {
|
||||
if (isAutocomplete(interaction)) {
|
||||
// autocomplete should not be stateful, they get no id
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isApplicationCommand(interaction)) {
|
||||
// for application commands, we create a new instance id
|
||||
const instance_id = crypto.randomUUID();
|
||||
return instance_id;
|
||||
}
|
||||
|
||||
const interact = interaction;
|
||||
const customId: string = interact.data.custom_id;
|
||||
const commandId = customId.split('_').pop();
|
||||
interaction;
|
||||
// command id should be a uuid
|
||||
if (commandId && /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(commandId)) {
|
||||
return commandId;
|
||||
}
|
||||
console.error(`Invalid command id extracted from interaction: ${customId}`);
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1,73 +1,73 @@
|
||||
import { type ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
import { getCommandName, isApplicationCommand, isAutocomplete, isMessageComponent, isModalSubmit } from './command-helpers';
|
||||
import type { PartialContext } from './command-context.type';
|
||||
import type { CommandHandler, ExecutableInteraction } from './command-handler';
|
||||
import { injectInteraction } from './command-injection';
|
||||
import { getCommandState } from './command-state';
|
||||
|
||||
export async function handleCommands(
|
||||
interaction: ExecutableInteraction,
|
||||
commands: Record<string, CommandHandler<ApplicationCommandStructure>>,
|
||||
ctx: PartialContext,
|
||||
) {
|
||||
ctx.state = await getCommandState(interaction, ctx);
|
||||
if (!ctx.state.name) {
|
||||
ctx.state.name = getCommandName(interaction);
|
||||
}
|
||||
|
||||
if (isAutocomplete(interaction) && ctx.state.name) {
|
||||
const acCommand = commands[ctx.state.name];
|
||||
return acCommand.execute(interaction, ctx as any);
|
||||
}
|
||||
|
||||
if (!ctx.state.id) {
|
||||
console.error(`No command ID found for interaction ${interaction.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const command = commands[ctx.state.name || ''];
|
||||
if (!command) {
|
||||
console.warn(`No command found for interaction: ${JSON.stringify(interaction, undefined, 2)}`);
|
||||
return;
|
||||
}
|
||||
cleanInteractionCustomIds(interaction, ctx.state.id);
|
||||
const [injectedInteraction, fullContext] = await injectInteraction(interaction, ctx);
|
||||
return command.execute(injectedInteraction, fullContext);
|
||||
}
|
||||
|
||||
export function initializeCommandHandling(commands: Record<string, CommandHandler<ApplicationCommandStructure>>, ctx: PartialContext) {
|
||||
ctx.client.on('interactionCreate', async (interaction) => {
|
||||
if (isApplicationCommand(interaction) || isModalSubmit(interaction) || isMessageComponent(interaction) || isAutocomplete(interaction)) {
|
||||
handleCommands(interaction, commands, ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cleanInteractionCustomIds(interaction: ExecutableInteraction, id: string) {
|
||||
if ('components' in interaction && Array.isArray(interaction.components) && id) {
|
||||
removeCommandIdFromComponentCustomIds(interaction.components, id);
|
||||
}
|
||||
if ('data' in interaction && id) {
|
||||
if ('custom_id' in interaction.data && typeof interaction.data.custom_id === 'string') {
|
||||
interaction.data.custom_id = interaction.data.custom_id.replace(`_${id}`, '');
|
||||
}
|
||||
if ('components' in interaction.data && Array.isArray(interaction.data.components)) {
|
||||
removeCommandIdFromComponentCustomIds(interaction.data.components as any, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeCommandIdFromComponentCustomIds(components: { custom_id?: string; components?: any[] }[], commandId: string) {
|
||||
components.forEach((component) => {
|
||||
if ('custom_id' in component) {
|
||||
component.custom_id = component.custom_id.replace(`_${commandId}`, '');
|
||||
}
|
||||
if ('components' in component && Array.isArray(component.components)) {
|
||||
removeCommandIdFromComponentCustomIds(component.components, commandId);
|
||||
}
|
||||
|
||||
if ('component' in component && 'custom_id' in (component as any).component && Array.isArray(component.components)) {
|
||||
(component.component as any).custom_id = (component.component as any).custom_id.replace(`_${commandId}`, '');
|
||||
}
|
||||
});
|
||||
}
|
||||
import { type ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
import { getCommandName, isApplicationCommand, isAutocomplete, isMessageComponent, isModalSubmit } from './command-helpers';
|
||||
import type { PartialContext } from './command-context.type';
|
||||
import type { CommandHandler, ExecutableInteraction } from './command-handler';
|
||||
import { injectInteraction } from './command-injection';
|
||||
import { getCommandState } from './command-state';
|
||||
|
||||
export async function handleCommands(
|
||||
interaction: ExecutableInteraction,
|
||||
commands: Record<string, CommandHandler<ApplicationCommandStructure>>,
|
||||
ctx: PartialContext,
|
||||
) {
|
||||
ctx.state = await getCommandState(interaction, ctx);
|
||||
if (!ctx.state.name) {
|
||||
ctx.state.name = getCommandName(interaction);
|
||||
}
|
||||
|
||||
if (isAutocomplete(interaction) && ctx.state.name) {
|
||||
const acCommand = commands[ctx.state.name];
|
||||
return acCommand.execute(interaction, ctx as any);
|
||||
}
|
||||
|
||||
if (!ctx.state.id) {
|
||||
console.error(`No command ID found for interaction ${interaction.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const command = commands[ctx.state.name || ''];
|
||||
if (!command) {
|
||||
console.warn(`No command found for interaction: ${JSON.stringify(interaction, undefined, 2)}`);
|
||||
return;
|
||||
}
|
||||
cleanInteractionCustomIds(interaction, ctx.state.id);
|
||||
const [injectedInteraction, fullContext] = await injectInteraction(interaction, ctx);
|
||||
return command.execute(injectedInteraction, fullContext);
|
||||
}
|
||||
|
||||
export function initializeCommandHandling(commands: Record<string, CommandHandler<ApplicationCommandStructure>>, ctx: PartialContext) {
|
||||
ctx.client.on('interactionCreate', async (interaction) => {
|
||||
if (isApplicationCommand(interaction) || isModalSubmit(interaction) || isMessageComponent(interaction) || isAutocomplete(interaction)) {
|
||||
handleCommands(interaction, commands, ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cleanInteractionCustomIds(interaction: ExecutableInteraction, id: string) {
|
||||
if ('components' in interaction && Array.isArray(interaction.components) && id) {
|
||||
removeCommandIdFromComponentCustomIds(interaction.components, id);
|
||||
}
|
||||
if ('data' in interaction && id) {
|
||||
if ('custom_id' in interaction.data && typeof interaction.data.custom_id === 'string') {
|
||||
interaction.data.custom_id = interaction.data.custom_id.replace(`_${id}`, '');
|
||||
}
|
||||
if ('components' in interaction.data && Array.isArray(interaction.data.components)) {
|
||||
removeCommandIdFromComponentCustomIds(interaction.data.components as any, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeCommandIdFromComponentCustomIds(components: { custom_id?: string; components?: any[] }[], commandId: string) {
|
||||
components.forEach((component) => {
|
||||
if ('custom_id' in component) {
|
||||
component.custom_id = component.custom_id.replace(`_${commandId}`, '');
|
||||
}
|
||||
if ('components' in component && Array.isArray(component.components)) {
|
||||
removeCommandIdFromComponentCustomIds(component.components, commandId);
|
||||
}
|
||||
|
||||
if ('component' in component && 'custom_id' in (component as any).component && Array.isArray(component.components)) {
|
||||
(component.component as any).custom_id = (component.component as any).custom_id.replace(`_${commandId}`, '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { Glob } from 'bun';
|
||||
import { join } from 'node:path';
|
||||
import type { CommandHandler } from './command-handler';
|
||||
import type { ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
|
||||
export async function importCommands(
|
||||
pattern: string = '**/*.command.{js,ts}',
|
||||
baseDir: string = join(process.cwd(), 'src'),
|
||||
commandRegistry: Record<string, CommandHandler<ApplicationCommandStructure>> = {},
|
||||
): Promise<Record<string, CommandHandler<ApplicationCommandStructure>>> {
|
||||
const glob = new Glob(pattern);
|
||||
|
||||
for await (const file of glob.scan({ cwd: baseDir, absolute: true })) {
|
||||
const command = (await import(file)).default as CommandHandler<ApplicationCommandStructure>;
|
||||
commandRegistry[command.definition.name] = command;
|
||||
}
|
||||
|
||||
return commandRegistry;
|
||||
}
|
||||
import { Glob } from 'bun';
|
||||
import { join } from 'node:path';
|
||||
import type { CommandHandler } from './command-handler';
|
||||
import type { ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
|
||||
export async function importCommands(
|
||||
pattern: string = '**/*.command.{js,ts}',
|
||||
baseDir: string = join(process.cwd(), 'src'),
|
||||
commandRegistry: Record<string, CommandHandler<ApplicationCommandStructure>> = {},
|
||||
): Promise<Record<string, CommandHandler<ApplicationCommandStructure>>> {
|
||||
const glob = new Glob(pattern);
|
||||
|
||||
for await (const file of glob.scan({ cwd: baseDir, absolute: true })) {
|
||||
const command = (await import(file)).default as CommandHandler<ApplicationCommandStructure>;
|
||||
commandRegistry[command.definition.name] = command;
|
||||
}
|
||||
|
||||
return commandRegistry;
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
import {
|
||||
Constants,
|
||||
type ApplicationCommandOptions,
|
||||
type ApplicationCommandOptionsBoolean,
|
||||
type ApplicationCommandOptionsInteger,
|
||||
type ApplicationCommandOptionsMentionable,
|
||||
type ApplicationCommandOptionsNumber,
|
||||
type ApplicationCommandOptionsRole,
|
||||
type ApplicationCommandOptionsString,
|
||||
type ApplicationCommandOptionsSubCommand,
|
||||
type ApplicationCommandOptionsSubCommandGroup,
|
||||
type ApplicationCommandOptionsUser,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type StringOptionDefinition = Omit<ApplicationCommandOptionsString, 'type'> & { autocomplete?: boolean };
|
||||
export function stringOption(options: StringOptionDefinition): ApplicationCommandOptionsString {
|
||||
const def = options as ApplicationCommandOptionsString;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.STRING;
|
||||
return def;
|
||||
}
|
||||
export type IntegerOptionDefinition = Omit<ApplicationCommandOptionsInteger, 'type'> & { autocomplete?: boolean };
|
||||
export function integerOption(options: IntegerOptionDefinition): ApplicationCommandOptionsInteger {
|
||||
const def = options as ApplicationCommandOptionsInteger;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.INTEGER;
|
||||
return def;
|
||||
}
|
||||
export type BooleanOptionDefinition = Omit<ApplicationCommandOptionsBoolean, 'type'>;
|
||||
export function booleanOption(options: BooleanOptionDefinition): ApplicationCommandOptionsBoolean {
|
||||
const def = options as ApplicationCommandOptionsBoolean;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.BOOLEAN;
|
||||
return def;
|
||||
}
|
||||
export type UserOptionDefinition = Omit<ApplicationCommandOptionsUser, 'type'> & { autocomplete?: boolean };
|
||||
export function userOption(options: UserOptionDefinition): ApplicationCommandOptionsUser {
|
||||
const def = options as ApplicationCommandOptionsUser;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.USER;
|
||||
return def;
|
||||
}
|
||||
export type ChannelOptionDefinition = Omit<ApplicationCommandOptions, 'type'> & { autocomplete?: boolean };
|
||||
export function channelOption(options: ChannelOptionDefinition): ApplicationCommandOptions {
|
||||
const def = options as ApplicationCommandOptions;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.CHANNEL;
|
||||
return def;
|
||||
}
|
||||
export type RoleOptionDefinition = Omit<ApplicationCommandOptionsRole, 'type'> & { autocomplete?: boolean };
|
||||
export function roleOption(options: RoleOptionDefinition): ApplicationCommandOptionsRole {
|
||||
const def = options as ApplicationCommandOptionsRole;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.ROLE;
|
||||
return def;
|
||||
}
|
||||
export type MentionableOptionDefinition = Omit<ApplicationCommandOptionsMentionable, 'type'>;
|
||||
export function mentionableOption(options: MentionableOptionDefinition): ApplicationCommandOptionsMentionable {
|
||||
const def = options as ApplicationCommandOptionsMentionable;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.MENTIONABLE;
|
||||
return def;
|
||||
}
|
||||
export type NumberOptionDefinition = Omit<ApplicationCommandOptionsNumber, 'type'> & { autocomplete?: boolean };
|
||||
export function numberOption(options: NumberOptionDefinition): ApplicationCommandOptionsNumber {
|
||||
const def = options as ApplicationCommandOptionsNumber;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.NUMBER;
|
||||
return def;
|
||||
}
|
||||
export type AttachmentOptionDefinition = Omit<ApplicationCommandOptions, 'type'>;
|
||||
export function attachmentOption(options: AttachmentOptionDefinition): ApplicationCommandOptions {
|
||||
const def = options as ApplicationCommandOptions;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.ATTACHMENT;
|
||||
return def;
|
||||
}
|
||||
export type SubCommandOptionDefinition = Omit<ApplicationCommandOptionsSubCommand, 'type'>;
|
||||
export function subCommandOption(options: SubCommandOptionDefinition): ApplicationCommandOptionsSubCommand {
|
||||
const def = options as ApplicationCommandOptionsSubCommand;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.SUB_COMMAND;
|
||||
return def;
|
||||
}
|
||||
export type SubCommandGroupOptionDefinition = Omit<ApplicationCommandOptionsSubCommandGroup, 'type'>;
|
||||
export function subCommandGroupOption(options: SubCommandGroupOptionDefinition): ApplicationCommandOptionsSubCommandGroup {
|
||||
const def = options as ApplicationCommandOptionsSubCommandGroup;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.SUB_COMMAND_GROUP;
|
||||
return def;
|
||||
}
|
||||
import {
|
||||
Constants,
|
||||
type ApplicationCommandOptions,
|
||||
type ApplicationCommandOptionsBoolean,
|
||||
type ApplicationCommandOptionsInteger,
|
||||
type ApplicationCommandOptionsMentionable,
|
||||
type ApplicationCommandOptionsNumber,
|
||||
type ApplicationCommandOptionsRole,
|
||||
type ApplicationCommandOptionsString,
|
||||
type ApplicationCommandOptionsSubCommand,
|
||||
type ApplicationCommandOptionsSubCommandGroup,
|
||||
type ApplicationCommandOptionsUser,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type StringOptionDefinition = Omit<ApplicationCommandOptionsString, 'type'> & { autocomplete?: boolean };
|
||||
export function stringOption(options: StringOptionDefinition): ApplicationCommandOptionsString {
|
||||
const def = options as ApplicationCommandOptionsString;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.STRING;
|
||||
return def;
|
||||
}
|
||||
export type IntegerOptionDefinition = Omit<ApplicationCommandOptionsInteger, 'type'> & { autocomplete?: boolean };
|
||||
export function integerOption(options: IntegerOptionDefinition): ApplicationCommandOptionsInteger {
|
||||
const def = options as ApplicationCommandOptionsInteger;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.INTEGER;
|
||||
return def;
|
||||
}
|
||||
export type BooleanOptionDefinition = Omit<ApplicationCommandOptionsBoolean, 'type'>;
|
||||
export function booleanOption(options: BooleanOptionDefinition): ApplicationCommandOptionsBoolean {
|
||||
const def = options as ApplicationCommandOptionsBoolean;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.BOOLEAN;
|
||||
return def;
|
||||
}
|
||||
export type UserOptionDefinition = Omit<ApplicationCommandOptionsUser, 'type'> & { autocomplete?: boolean };
|
||||
export function userOption(options: UserOptionDefinition): ApplicationCommandOptionsUser {
|
||||
const def = options as ApplicationCommandOptionsUser;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.USER;
|
||||
return def;
|
||||
}
|
||||
export type ChannelOptionDefinition = Omit<ApplicationCommandOptions, 'type'> & { autocomplete?: boolean };
|
||||
export function channelOption(options: ChannelOptionDefinition): ApplicationCommandOptions {
|
||||
const def = options as ApplicationCommandOptions;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.CHANNEL;
|
||||
return def;
|
||||
}
|
||||
export type RoleOptionDefinition = Omit<ApplicationCommandOptionsRole, 'type'> & { autocomplete?: boolean };
|
||||
export function roleOption(options: RoleOptionDefinition): ApplicationCommandOptionsRole {
|
||||
const def = options as ApplicationCommandOptionsRole;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.ROLE;
|
||||
return def;
|
||||
}
|
||||
export type MentionableOptionDefinition = Omit<ApplicationCommandOptionsMentionable, 'type'>;
|
||||
export function mentionableOption(options: MentionableOptionDefinition): ApplicationCommandOptionsMentionable {
|
||||
const def = options as ApplicationCommandOptionsMentionable;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.MENTIONABLE;
|
||||
return def;
|
||||
}
|
||||
export type NumberOptionDefinition = Omit<ApplicationCommandOptionsNumber, 'type'> & { autocomplete?: boolean };
|
||||
export function numberOption(options: NumberOptionDefinition): ApplicationCommandOptionsNumber {
|
||||
const def = options as ApplicationCommandOptionsNumber;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.NUMBER;
|
||||
return def;
|
||||
}
|
||||
export type AttachmentOptionDefinition = Omit<ApplicationCommandOptions, 'type'>;
|
||||
export function attachmentOption(options: AttachmentOptionDefinition): ApplicationCommandOptions {
|
||||
const def = options as ApplicationCommandOptions;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.ATTACHMENT;
|
||||
return def;
|
||||
}
|
||||
export type SubCommandOptionDefinition = Omit<ApplicationCommandOptionsSubCommand, 'type'>;
|
||||
export function subCommandOption(options: SubCommandOptionDefinition): ApplicationCommandOptionsSubCommand {
|
||||
const def = options as ApplicationCommandOptionsSubCommand;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.SUB_COMMAND;
|
||||
return def;
|
||||
}
|
||||
export type SubCommandGroupOptionDefinition = Omit<ApplicationCommandOptionsSubCommandGroup, 'type'>;
|
||||
export function subCommandGroupOption(options: SubCommandGroupOptionDefinition): ApplicationCommandOptionsSubCommandGroup {
|
||||
const def = options as ApplicationCommandOptionsSubCommandGroup;
|
||||
def.type = Constants.ApplicationCommandOptionTypes.SUB_COMMAND_GROUP;
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ApplicationCommandStructure, Client } from '@projectdysnomia/dysnomia';
|
||||
|
||||
export async function registerCommands(client: Client, commands: ApplicationCommandStructure[]) {
|
||||
if (!client) throw new Error('Client not initialized');
|
||||
if (!(await client.getCommands()).length || process.env.RESET_COMMANDS === 'true' || process.env.NODE_ENV === 'development') {
|
||||
console.debug('Registering commands...');
|
||||
const response = await client.bulkEditCommands(commands);
|
||||
console.debug(`Registered ${response.length} commands.`);
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
import type { ApplicationCommandStructure, Client } from '@projectdysnomia/dysnomia';
|
||||
|
||||
export async function registerCommands(client: Client, commands: ApplicationCommandStructure[]) {
|
||||
if (!client) throw new Error('Client not initialized');
|
||||
if (!(await client.getCommands()).length || process.env.RESET_COMMANDS === 'true' || process.env.NODE_ENV === 'development') {
|
||||
console.debug('Registering commands...');
|
||||
const response = await client.bulkEditCommands(commands);
|
||||
console.debug(`Registered ${response.length} commands.`);
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export * from './text';
|
||||
export * from './text';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export const WHITE_SPACE = ' '; // non-breaking space
|
||||
export const BREAKING_WHITE_SPACE = '\u200B';
|
||||
export const WHITE_SPACE = ' '; // non-breaking space
|
||||
export const BREAKING_WHITE_SPACE = '\u200B';
|
||||
|
||||
@@ -1,314 +1,314 @@
|
||||
import {
|
||||
Constants,
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type ActionRowItem = Button | StringSelectMenu | UserSelectMenu | RoleSelectMenu | MentionableSelectMenu | ChannelSelectMenu;
|
||||
export const createActionRow = (...components: ActionRowItem[]): ActionRow => ({
|
||||
type: Constants.ComponentTypes.ACTION_ROW,
|
||||
components,
|
||||
});
|
||||
|
||||
export enum ButtonStyle {
|
||||
PRIMARY = 1,
|
||||
SECONDARY = 2,
|
||||
SUCCESS = 3,
|
||||
DANGER = 4,
|
||||
}
|
||||
|
||||
export interface ButtonOptions {
|
||||
style?: ButtonStyle;
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const createButton = (label: string, custom_id: string, options?: ButtonOptions): InteractionButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: options?.style ?? Constants.ButtonStyles.PRIMARY,
|
||||
label,
|
||||
custom_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface URLButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const createURLButton = (label: string, url: string, options?: URLButtonOptions): URLButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.LINK,
|
||||
label,
|
||||
url,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface PremiumButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const createPremiumButton = (sku_id: string, options?: PremiumButtonOptions): PremiumButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.PREMIUM,
|
||||
sku_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface StringSelectOpts {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
}
|
||||
|
||||
export interface StringSelectOption {
|
||||
label: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
emoji?: {
|
||||
name?: string;
|
||||
id?: string;
|
||||
animated?: boolean;
|
||||
};
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
export const createStringSelect = (
|
||||
custom_id: string,
|
||||
selectOpts: StringSelectOpts,
|
||||
...options: StringSelectOption[]
|
||||
): StringSelectMenu => ({
|
||||
type: Constants.ComponentTypes.STRING_SELECT,
|
||||
custom_id,
|
||||
options,
|
||||
placeholder: selectOpts.placeholder ?? '',
|
||||
min_values: selectOpts.min_values ?? 1,
|
||||
max_values: selectOpts.max_values ?? 1,
|
||||
disabled: selectOpts.disabled ?? false,
|
||||
required: selectOpts.required ?? false, // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
});
|
||||
|
||||
export interface TextInputOptions {
|
||||
isParagraph?: boolean;
|
||||
label?: string;
|
||||
min_length?: number;
|
||||
max_length?: number;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export const createTextInput = (custom_id: string, options?: TextInputOptions): TextInput => ({
|
||||
type: Constants.ComponentTypes.TEXT_INPUT,
|
||||
custom_id,
|
||||
style: options.isParagraph ? Constants.TextInputStyles.PARAGRAPH : Constants.TextInputStyles.SHORT,
|
||||
label: options?.label ?? '',
|
||||
min_length: options?.min_length ?? 0,
|
||||
max_length: options?.max_length ?? 4000,
|
||||
required: options?.required ?? false,
|
||||
value: options?.value ?? '',
|
||||
placeholder: options?.placeholder ?? '',
|
||||
});
|
||||
|
||||
export interface UserSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'user' }>;
|
||||
}
|
||||
|
||||
export const createUserSelect = (custom_id: string, options?: UserSelectOptions): UserSelectMenu => ({
|
||||
type: Constants.ComponentTypes.USER_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface RoleSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'role' }>;
|
||||
}
|
||||
|
||||
export const createRoleSelect = (custom_id: string, options?: RoleSelectOptions): RoleSelectMenu => ({
|
||||
type: Constants.ComponentTypes.ROLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface MentionableSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'user' | 'role' }>;
|
||||
}
|
||||
|
||||
export const createMentionableSelect = (custom_id: string, options?: MentionableSelectOptions): MentionableSelectMenu => ({
|
||||
type: Constants.ComponentTypes.MENTIONABLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface ChannelSelectOptions {
|
||||
channel_types?: GuildChannelTypes[];
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'channel' }>;
|
||||
}
|
||||
|
||||
export const createChannelSelect = (custom_id: string, options?: ChannelSelectOptions): ChannelSelectMenu => ({
|
||||
type: Constants.ComponentTypes.CHANNEL_SELECT,
|
||||
custom_id,
|
||||
channel_types: options?.channel_types ?? [],
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface SectionOptions {
|
||||
components: Array<TextDisplayComponent>;
|
||||
accessory: Button | ThumbnailComponent;
|
||||
}
|
||||
|
||||
export const createSection = (accessory: Button | ThumbnailComponent, ...components: Array<TextDisplayComponent>): SectionComponent => ({
|
||||
type: Constants.ComponentTypes.SECTION,
|
||||
accessory,
|
||||
components,
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a text display component where the text will be displayed similar to a message: supports markdown
|
||||
* @param content The text content to display.
|
||||
* @returns The created text display component.
|
||||
*/
|
||||
export const createTextDisplay = (content: string) => ({
|
||||
type: Constants.ComponentTypes.TEXT_DISPLAY,
|
||||
content,
|
||||
});
|
||||
|
||||
export interface ThumbnailOptions {
|
||||
media: {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
};
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const createThumbnail = (url: string, description?: string, spoiler?: boolean): ThumbnailComponent => ({
|
||||
type: Constants.ComponentTypes.THUMBNAIL,
|
||||
media: {
|
||||
url,
|
||||
},
|
||||
description,
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export interface MediaItem {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const createMediaGallery = (...items: MediaItem[]): MediaGalleryComponent => ({
|
||||
type: Constants.ComponentTypes.MEDIA_GALLERY,
|
||||
items: items.map((item) => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
media: { url: item.url },
|
||||
description: item.description,
|
||||
spoiler: item.spoiler,
|
||||
})),
|
||||
});
|
||||
|
||||
export interface FileOptions {
|
||||
url: string; // Supports only attachment://<filename> references
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const createFile = (url: string, spoiler?: boolean): FileComponent => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
file: {
|
||||
url,
|
||||
},
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export enum Padding {
|
||||
SMALL = 1,
|
||||
LARGE = 2,
|
||||
}
|
||||
|
||||
export interface SeparatorOptions {
|
||||
divider?: boolean;
|
||||
spacing?: Padding;
|
||||
}
|
||||
export const createSeparator = (spacing?: Padding, divider?: boolean): SeparatorComponent => ({
|
||||
type: Constants.ComponentTypes.SEPARATOR,
|
||||
divider,
|
||||
spacing: spacing ?? Padding.SMALL,
|
||||
});
|
||||
|
||||
export interface ContainerOptions {
|
||||
accent_color?: number;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export type ContainerItems =
|
||||
| ActionRow
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent;
|
||||
|
||||
export const createContainer = (options: ContainerOptions, ...components: ContainerItems[]): ContainerComponent => ({
|
||||
type: Constants.ComponentTypes.CONTAINER,
|
||||
...options,
|
||||
components,
|
||||
});
|
||||
|
||||
export const createModalLabel = (label: string, component: TextInput | StringSelectMenu): LabelComponent => ({
|
||||
type: Constants.ComponentTypes.LABEL,
|
||||
label,
|
||||
component,
|
||||
});
|
||||
import {
|
||||
Constants,
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type ActionRowItem = Button | StringSelectMenu | UserSelectMenu | RoleSelectMenu | MentionableSelectMenu | ChannelSelectMenu;
|
||||
export const createActionRow = (...components: ActionRowItem[]): ActionRow => ({
|
||||
type: Constants.ComponentTypes.ACTION_ROW,
|
||||
components,
|
||||
});
|
||||
|
||||
export enum ButtonStyle {
|
||||
PRIMARY = 1,
|
||||
SECONDARY = 2,
|
||||
SUCCESS = 3,
|
||||
DANGER = 4,
|
||||
}
|
||||
|
||||
export interface ButtonOptions {
|
||||
style?: ButtonStyle;
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const createButton = (label: string, custom_id: string, options?: ButtonOptions): InteractionButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: options?.style ?? Constants.ButtonStyles.PRIMARY,
|
||||
label,
|
||||
custom_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface URLButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const createURLButton = (label: string, url: string, options?: URLButtonOptions): URLButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.LINK,
|
||||
label,
|
||||
url,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface PremiumButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const createPremiumButton = (sku_id: string, options?: PremiumButtonOptions): PremiumButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.PREMIUM,
|
||||
sku_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface StringSelectOpts {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
}
|
||||
|
||||
export interface StringSelectOption {
|
||||
label: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
emoji?: {
|
||||
name?: string;
|
||||
id?: string;
|
||||
animated?: boolean;
|
||||
};
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
export const createStringSelect = (
|
||||
custom_id: string,
|
||||
selectOpts: StringSelectOpts,
|
||||
...options: StringSelectOption[]
|
||||
): StringSelectMenu => ({
|
||||
type: Constants.ComponentTypes.STRING_SELECT,
|
||||
custom_id,
|
||||
options,
|
||||
placeholder: selectOpts.placeholder ?? '',
|
||||
min_values: selectOpts.min_values ?? 1,
|
||||
max_values: selectOpts.max_values ?? 1,
|
||||
disabled: selectOpts.disabled ?? false,
|
||||
required: selectOpts.required ?? false, // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
});
|
||||
|
||||
export interface TextInputOptions {
|
||||
isParagraph?: boolean;
|
||||
label?: string;
|
||||
min_length?: number;
|
||||
max_length?: number;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export const createTextInput = (custom_id: string, options?: TextInputOptions): TextInput => ({
|
||||
type: Constants.ComponentTypes.TEXT_INPUT,
|
||||
custom_id,
|
||||
style: options.isParagraph ? Constants.TextInputStyles.PARAGRAPH : Constants.TextInputStyles.SHORT,
|
||||
label: options?.label ?? '',
|
||||
min_length: options?.min_length ?? 0,
|
||||
max_length: options?.max_length ?? 4000,
|
||||
required: options?.required ?? false,
|
||||
value: options?.value ?? '',
|
||||
placeholder: options?.placeholder ?? '',
|
||||
});
|
||||
|
||||
export interface UserSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'user' }>;
|
||||
}
|
||||
|
||||
export const createUserSelect = (custom_id: string, options?: UserSelectOptions): UserSelectMenu => ({
|
||||
type: Constants.ComponentTypes.USER_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface RoleSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'role' }>;
|
||||
}
|
||||
|
||||
export const createRoleSelect = (custom_id: string, options?: RoleSelectOptions): RoleSelectMenu => ({
|
||||
type: Constants.ComponentTypes.ROLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface MentionableSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'user' | 'role' }>;
|
||||
}
|
||||
|
||||
export const createMentionableSelect = (custom_id: string, options?: MentionableSelectOptions): MentionableSelectMenu => ({
|
||||
type: Constants.ComponentTypes.MENTIONABLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface ChannelSelectOptions {
|
||||
channel_types?: GuildChannelTypes[];
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
default_values?: Array<{ id: string; type: 'channel' }>;
|
||||
}
|
||||
|
||||
export const createChannelSelect = (custom_id: string, options?: ChannelSelectOptions): ChannelSelectMenu => ({
|
||||
type: Constants.ComponentTypes.CHANNEL_SELECT,
|
||||
custom_id,
|
||||
channel_types: options?.channel_types ?? [],
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface SectionOptions {
|
||||
components: Array<TextDisplayComponent>;
|
||||
accessory: Button | ThumbnailComponent;
|
||||
}
|
||||
|
||||
export const createSection = (accessory: Button | ThumbnailComponent, ...components: Array<TextDisplayComponent>): SectionComponent => ({
|
||||
type: Constants.ComponentTypes.SECTION,
|
||||
accessory,
|
||||
components,
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a text display component where the text will be displayed similar to a message: supports markdown
|
||||
* @param content The text content to display.
|
||||
* @returns The created text display component.
|
||||
*/
|
||||
export const createTextDisplay = (content: string) => ({
|
||||
type: Constants.ComponentTypes.TEXT_DISPLAY,
|
||||
content,
|
||||
});
|
||||
|
||||
export interface ThumbnailOptions {
|
||||
media: {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
};
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const createThumbnail = (url: string, description?: string, spoiler?: boolean): ThumbnailComponent => ({
|
||||
type: Constants.ComponentTypes.THUMBNAIL,
|
||||
media: {
|
||||
url,
|
||||
},
|
||||
description,
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export interface MediaItem {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const createMediaGallery = (...items: MediaItem[]): MediaGalleryComponent => ({
|
||||
type: Constants.ComponentTypes.MEDIA_GALLERY,
|
||||
items: items.map((item) => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
media: { url: item.url },
|
||||
description: item.description,
|
||||
spoiler: item.spoiler,
|
||||
})),
|
||||
});
|
||||
|
||||
export interface FileOptions {
|
||||
url: string; // Supports only attachment://<filename> references
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const createFile = (url: string, spoiler?: boolean): FileComponent => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
file: {
|
||||
url,
|
||||
},
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export enum Padding {
|
||||
SMALL = 1,
|
||||
LARGE = 2,
|
||||
}
|
||||
|
||||
export interface SeparatorOptions {
|
||||
divider?: boolean;
|
||||
spacing?: Padding;
|
||||
}
|
||||
export const createSeparator = (spacing?: Padding, divider?: boolean): SeparatorComponent => ({
|
||||
type: Constants.ComponentTypes.SEPARATOR,
|
||||
divider,
|
||||
spacing: spacing ?? Padding.SMALL,
|
||||
});
|
||||
|
||||
export interface ContainerOptions {
|
||||
accent_color?: number;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export type ContainerItems =
|
||||
| ActionRow
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent;
|
||||
|
||||
export const createContainer = (options: ContainerOptions, ...components: ContainerItems[]): ContainerComponent => ({
|
||||
type: Constants.ComponentTypes.CONTAINER,
|
||||
...options,
|
||||
components,
|
||||
});
|
||||
|
||||
export const createModalLabel = (label: string, component: TextInput | StringSelectMenu): LabelComponent => ({
|
||||
type: Constants.ComponentTypes.LABEL,
|
||||
label,
|
||||
component,
|
||||
});
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import {
|
||||
Constants,
|
||||
type ComponentBase,
|
||||
type ModalSubmitInteractionDataLabelComponent,
|
||||
type ModalSubmitInteractionDataStringSelectComponent,
|
||||
type ModalSubmitInteractionDataTextInputComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export function isModalLabel(component: ComponentBase): component is ModalSubmitInteractionDataLabelComponent {
|
||||
return component.type === Constants.ComponentTypes.LABEL;
|
||||
}
|
||||
|
||||
export function isModalTextInput(component: ComponentBase): component is ModalSubmitInteractionDataTextInputComponent {
|
||||
return component.type === Constants.ComponentTypes.TEXT_INPUT;
|
||||
}
|
||||
|
||||
export function isModalSelect(component: ComponentBase): component is ModalSubmitInteractionDataStringSelectComponent {
|
||||
return component.type === Constants.ComponentTypes.STRING_SELECT;
|
||||
}
|
||||
|
||||
export function componentHasIdPrefix(component: ComponentBase, prefix: string): boolean {
|
||||
return (isModalTextInput(component) || isModalSelect(component)) && component.custom_id.startsWith(prefix);
|
||||
}
|
||||
import {
|
||||
Constants,
|
||||
type ComponentBase,
|
||||
type ModalSubmitInteractionDataLabelComponent,
|
||||
type ModalSubmitInteractionDataStringSelectComponent,
|
||||
type ModalSubmitInteractionDataTextInputComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export function isModalLabel(component: ComponentBase): component is ModalSubmitInteractionDataLabelComponent {
|
||||
return component.type === Constants.ComponentTypes.LABEL;
|
||||
}
|
||||
|
||||
export function isModalTextInput(component: ComponentBase): component is ModalSubmitInteractionDataTextInputComponent {
|
||||
return component.type === Constants.ComponentTypes.TEXT_INPUT;
|
||||
}
|
||||
|
||||
export function isModalSelect(component: ComponentBase): component is ModalSubmitInteractionDataStringSelectComponent {
|
||||
return component.type === Constants.ComponentTypes.STRING_SELECT;
|
||||
}
|
||||
|
||||
export function componentHasIdPrefix(component: ComponentBase, prefix: string): boolean {
|
||||
return (isModalTextInput(component) || isModalSelect(component)) && component.custom_id.startsWith(prefix);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './helpers';
|
||||
export * from './builders';
|
||||
export * from './helpers';
|
||||
export * from './builders';
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
import { importCommands, initializeCommandHandling, registerCommands } from '@commands';
|
||||
import { Client } from '@projectdysnomia/dysnomia';
|
||||
import kv, { asyncKV } from '@star-kitten/util/kv.js';
|
||||
import type { KVStore } from './kv-store.type.ts';
|
||||
import type { Cache } from './cache.type.ts';
|
||||
|
||||
export interface DiscordBotOptions {
|
||||
token?: string;
|
||||
intents?: number[];
|
||||
commandPattern?: string;
|
||||
commandBaseDir?: string;
|
||||
keyStore?: KVStore;
|
||||
cache?: Cache;
|
||||
onError?: (error: Error) => void;
|
||||
onReady?: () => void;
|
||||
}
|
||||
|
||||
export function startDiscordBot({
|
||||
token = process.env.DISCORD_BOT_TOKEN || '',
|
||||
intents = [],
|
||||
commandPattern = '**/*.command.{js,ts}',
|
||||
commandBaseDir = 'src',
|
||||
keyStore = asyncKV,
|
||||
cache = kv,
|
||||
onError,
|
||||
onReady,
|
||||
}: DiscordBotOptions = {}): Client {
|
||||
const client = new Client(`Bot ${token}`, {
|
||||
gateway: {
|
||||
intents,
|
||||
},
|
||||
});
|
||||
|
||||
client.on('ready', async () => {
|
||||
console.debug(`Logged in as ${client.user?.username}#${client.user?.discriminator}`);
|
||||
onReady?.();
|
||||
const commands = await importCommands(commandPattern, commandBaseDir);
|
||||
await registerCommands(
|
||||
client,
|
||||
Object.values(commands).map((cmd) => cmd.definition),
|
||||
);
|
||||
initializeCommandHandling(commands, { client, cache, kv: keyStore });
|
||||
console.debug('Bot is ready and command handling is initialized.');
|
||||
});
|
||||
|
||||
client.on('error', (error) => {
|
||||
console.error('An error occurred:', error);
|
||||
onError?.(error);
|
||||
});
|
||||
|
||||
client.connect().catch(console.error);
|
||||
|
||||
return client;
|
||||
}
|
||||
import { importCommands, initializeCommandHandling, registerCommands } from '@commands';
|
||||
import { Client } from '@projectdysnomia/dysnomia';
|
||||
import kv, { asyncKV } from '@star-kitten/util/kv.js';
|
||||
import type { KVStore } from './kv-store.type.ts';
|
||||
import type { Cache } from './cache.type.ts';
|
||||
|
||||
export interface DiscordBotOptions {
|
||||
token?: string;
|
||||
intents?: number[];
|
||||
commandPattern?: string;
|
||||
commandBaseDir?: string;
|
||||
keyStore?: KVStore;
|
||||
cache?: Cache;
|
||||
onError?: (error: Error) => void;
|
||||
onReady?: () => void;
|
||||
}
|
||||
|
||||
export function startDiscordBot({
|
||||
token = process.env.DISCORD_BOT_TOKEN || '',
|
||||
intents = [],
|
||||
commandPattern = '**/*.command.{js,ts}',
|
||||
commandBaseDir = 'src',
|
||||
keyStore = asyncKV,
|
||||
cache = kv,
|
||||
onError,
|
||||
onReady,
|
||||
}: DiscordBotOptions = {}): Client {
|
||||
const client = new Client(`Bot ${token}`, {
|
||||
gateway: {
|
||||
intents,
|
||||
},
|
||||
});
|
||||
|
||||
client.on('ready', async () => {
|
||||
console.debug(`Logged in as ${client.user?.username}#${client.user?.discriminator}`);
|
||||
onReady?.();
|
||||
const commands = await importCommands(commandPattern, commandBaseDir);
|
||||
await registerCommands(
|
||||
client,
|
||||
Object.values(commands).map((cmd) => cmd.definition),
|
||||
);
|
||||
initializeCommandHandling(commands, { client, cache, kv: keyStore });
|
||||
console.debug('Bot is ready and command handling is initialized.');
|
||||
});
|
||||
|
||||
client.on('error', (error) => {
|
||||
console.error('An error occurred:', error);
|
||||
onError?.(error);
|
||||
});
|
||||
|
||||
client.connect().catch(console.error);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface Cache {
|
||||
get: <T>(key: string) => T | undefined;
|
||||
set: <T>(key: string, value: T, ttl?: number | string) => boolean;
|
||||
del: (key: string | string[]) => number;
|
||||
has: (key: string) => boolean;
|
||||
}
|
||||
export interface Cache {
|
||||
get: <T>(key: string) => T | undefined;
|
||||
set: <T>(key: string, value: T, ttl?: number | string) => boolean;
|
||||
del: (key: string | string[]) => number;
|
||||
has: (key: string) => boolean;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './bot';
|
||||
export * from './cache.type';
|
||||
export * from './kv-store.type.ts';
|
||||
export * from './bot';
|
||||
export * from './cache.type';
|
||||
export * from './kv-store.type.ts';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export interface KVStore {
|
||||
get: <T>(key: string) => Promise<T | undefined>;
|
||||
set: (key: string, value: any) => Promise<boolean>;
|
||||
delete: (key: string) => Promise<number>;
|
||||
has: (key: string) => Promise<boolean>;
|
||||
clear: () => Promise<void>;
|
||||
}
|
||||
export interface KVStore {
|
||||
get: <T>(key: string) => Promise<T | undefined>;
|
||||
set: (key: string, value: any) => Promise<boolean>;
|
||||
delete: (key: string) => Promise<number>;
|
||||
has: (key: string) => Promise<boolean>;
|
||||
clear: () => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -2,3 +2,5 @@ export * from './locales';
|
||||
export * from './commands';
|
||||
export * from './core';
|
||||
export * from './jsx';
|
||||
export * from './components';
|
||||
export * from './pages';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export function createElement(tag: string, attrs: Record<string, any> = {}, ...children: any[]) {
|
||||
return {
|
||||
tag,
|
||||
attrs,
|
||||
children,
|
||||
};
|
||||
}
|
||||
export function createElement(tag: string, attrs: Record<string, any> = {}, ...children: any[]) {
|
||||
return {
|
||||
tag,
|
||||
attrs,
|
||||
children,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './parser';
|
||||
export * from './createElement';
|
||||
export * from './parser';
|
||||
export * from './createElement';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { describe, it, expect } from 'bun:test';
|
||||
import { parseJSDFile } from './parser_new';
|
||||
import path from 'node:path';
|
||||
|
||||
describe('parseJSDFile', () => {
|
||||
it('should parse a JSD file', async () => {
|
||||
const result = await parseJSDFile(path.join(__dirname, '../../fixtures/jsd/test.tsd'));
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
});
|
||||
import { describe, it, expect } from 'bun:test';
|
||||
import { parseJSDFile } from './parser_new';
|
||||
import path from 'node:path';
|
||||
|
||||
describe('parseJSDFile', () => {
|
||||
it('should parse a JSD file', async () => {
|
||||
const result = await parseJSDFile(path.join(__dirname, '../../fixtures/jsd/test.tsd'));
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import parse, { type DOMNode } from 'html-dom-parser';
|
||||
import type { ChildNode } from 'domhandler';
|
||||
|
||||
const JSD_STRING = /\(\s*(<.*)>\s*\)/gs;
|
||||
|
||||
export async function parseJSDFile(filename: string) {
|
||||
const content = (await fs.readFile(filename)).toString();
|
||||
|
||||
const matches = JSD_STRING.exec(content);
|
||||
if (matches) {
|
||||
let html = matches[1] + '>';
|
||||
const root = parse(html);
|
||||
const translated = translate(root[0]);
|
||||
const str = content.replace(matches[1] + '>', translated);
|
||||
await fs.writeFile(filename.replace('.tsd', '.ts'), str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
interface state {
|
||||
inInterpolation?: boolean;
|
||||
children?: string[][];
|
||||
parent?: Text[];
|
||||
}
|
||||
|
||||
function translate(root: DOMNode | ChildNode | null, state: state = {}): string | null {
|
||||
if (!root || typeof root !== 'object') return null;
|
||||
|
||||
let children = [];
|
||||
if ('children' in root && Array.isArray(root.children) && root.children.length > 0) {
|
||||
for (const child of root.children) {
|
||||
const translated = translate(child, state);
|
||||
if (translated) {
|
||||
if (state.inInterpolation && state.parent[state.children.length - 1] === child) {
|
||||
state.children[state.children.length - 1].push(translated);
|
||||
} else {
|
||||
children.push(translated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ('nodeType' in root && root.nodeType === 3) {
|
||||
if (root.data.trim() === '') return null;
|
||||
return parseText(root.data.trim(), state, root);
|
||||
}
|
||||
|
||||
if ('name' in root && root.name) {
|
||||
let tagName = root.name || 'unknown';
|
||||
let attrs = 'attribs' in root ? root.attribs : {};
|
||||
return `StarKitten.createElement("${tagName}", ${JSON.stringify(attrs)}${children.length > 0 ? ', ' + children.join(', ') : ''})`;
|
||||
}
|
||||
}
|
||||
|
||||
const JSD_INTERPOLATION = /\{(.+)\}/gs;
|
||||
const JSD_START_EXP_INTERPOLATION = /\{(.+)\(/gs;
|
||||
const JSD_END_EXP_INTERPOLATION = /\)(.+)\}/gs;
|
||||
|
||||
function parseText(text: string, state: state = {}, parent: Text = {}): string {
|
||||
let interpolations = text.match(JSD_INTERPOLATION);
|
||||
if (!interpolations) {
|
||||
if (text.match(JSD_START_EXP_INTERPOLATION)) {
|
||||
state.inInterpolation = true;
|
||||
state.children = state.children || [[]];
|
||||
state.parent = state.parent || [];
|
||||
state.parent.push(parent);
|
||||
return text.substring(1, text.length - 1);
|
||||
} else if (text.match(JSD_END_EXP_INTERPOLATION)) {
|
||||
const combined = state.children?.[state.children.length - 1].join(' ');
|
||||
state.children?.[state.children.length - 1].splice(0);
|
||||
state.children?.pop();
|
||||
state.parent?.pop();
|
||||
if (state.children.length === 0) {
|
||||
state.inInterpolation = false;
|
||||
return combined + ' ' + text.substring(1, text.length - 1);
|
||||
}
|
||||
}
|
||||
return `"${text}"`;
|
||||
} else {
|
||||
text = replaceInterpolations(text);
|
||||
return `"${text}"`;
|
||||
}
|
||||
}
|
||||
|
||||
function replaceInterpolations(text: string, isOnJSON: boolean = false) {
|
||||
let interpolations = null;
|
||||
|
||||
while ((interpolations = JSD_INTERPOLATION.exec(text))) {
|
||||
if (isOnJSON) {
|
||||
text = text.replace(`"{${interpolations[1]}}"`, interpolations[1]);
|
||||
} else {
|
||||
text = text.replace(`{${interpolations[1]}}`, `"+ ${interpolations[1]} +"`);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
import fs from 'node:fs/promises';
|
||||
import parse, { type DOMNode } from 'html-dom-parser';
|
||||
import type { ChildNode } from 'domhandler';
|
||||
|
||||
const JSD_STRING = /\(\s*(<.*)>\s*\)/gs;
|
||||
|
||||
export async function parseJSDFile(filename: string) {
|
||||
const content = (await fs.readFile(filename)).toString();
|
||||
|
||||
const matches = JSD_STRING.exec(content);
|
||||
if (matches) {
|
||||
let html = matches[1] + '>';
|
||||
const root = parse(html);
|
||||
const translated = translate(root[0]);
|
||||
const str = content.replace(matches[1] + '>', translated);
|
||||
await fs.writeFile(filename.replace('.tsd', '.ts'), str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
interface state {
|
||||
inInterpolation?: boolean;
|
||||
children?: string[][];
|
||||
parent?: Text[];
|
||||
}
|
||||
|
||||
function translate(root: DOMNode | ChildNode | null, state: state = {}): string | null {
|
||||
if (!root || typeof root !== 'object') return null;
|
||||
|
||||
let children = [];
|
||||
if ('children' in root && Array.isArray(root.children) && root.children.length > 0) {
|
||||
for (const child of root.children) {
|
||||
const translated = translate(child, state);
|
||||
if (translated) {
|
||||
if (state.inInterpolation && state.parent[state.children.length - 1] === child) {
|
||||
state.children[state.children.length - 1].push(translated);
|
||||
} else {
|
||||
children.push(translated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ('nodeType' in root && root.nodeType === 3) {
|
||||
if (root.data.trim() === '') return null;
|
||||
return parseText(root.data.trim(), state, root);
|
||||
}
|
||||
|
||||
if ('name' in root && root.name) {
|
||||
let tagName = root.name || 'unknown';
|
||||
let attrs = 'attribs' in root ? root.attribs : {};
|
||||
return `StarKitten.createElement("${tagName}", ${JSON.stringify(attrs)}${children.length > 0 ? ', ' + children.join(', ') : ''})`;
|
||||
}
|
||||
}
|
||||
|
||||
const JSD_INTERPOLATION = /\{(.+)\}/gs;
|
||||
const JSD_START_EXP_INTERPOLATION = /\{(.+)\(/gs;
|
||||
const JSD_END_EXP_INTERPOLATION = /\)(.+)\}/gs;
|
||||
|
||||
function parseText(text: string, state: state = {}, parent: Text = {}): string {
|
||||
let interpolations = text.match(JSD_INTERPOLATION);
|
||||
if (!interpolations) {
|
||||
if (text.match(JSD_START_EXP_INTERPOLATION)) {
|
||||
state.inInterpolation = true;
|
||||
state.children = state.children || [[]];
|
||||
state.parent = state.parent || [];
|
||||
state.parent.push(parent);
|
||||
return text.substring(1, text.length - 1);
|
||||
} else if (text.match(JSD_END_EXP_INTERPOLATION)) {
|
||||
const combined = state.children?.[state.children.length - 1].join(' ');
|
||||
state.children?.[state.children.length - 1].splice(0);
|
||||
state.children?.pop();
|
||||
state.parent?.pop();
|
||||
if (state.children.length === 0) {
|
||||
state.inInterpolation = false;
|
||||
return combined + ' ' + text.substring(1, text.length - 1);
|
||||
}
|
||||
}
|
||||
return `"${text}"`;
|
||||
} else {
|
||||
text = replaceInterpolations(text);
|
||||
return `"${text}"`;
|
||||
}
|
||||
}
|
||||
|
||||
function replaceInterpolations(text: string, isOnJSON: boolean = false) {
|
||||
let interpolations = null;
|
||||
|
||||
while ((interpolations = JSD_INTERPOLATION.exec(text))) {
|
||||
if (isOnJSON) {
|
||||
text = text.replace(`"{${interpolations[1]}}"`, interpolations[1]);
|
||||
} else {
|
||||
text = text.replace(`{${interpolations[1]}}`, `"+ ${interpolations[1]} +"`);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import * as acorn from 'acorn';
|
||||
import jsx from 'acorn-jsx';
|
||||
|
||||
const JSD_STRING = /\(\s*(<.*)>\s*\)/gs;
|
||||
|
||||
const parser = acorn.Parser.extend(jsx());
|
||||
|
||||
export async function parseJSDFile(filename: string) {
|
||||
const content = (await fs.readFile(filename)).toString();
|
||||
|
||||
const matches = JSD_STRING.exec(content);
|
||||
if (matches) {
|
||||
const jsxc = matches[1] + '>';
|
||||
const ast = parser.parse(jsxc, { ecmaVersion: 2020, sourceType: 'module' });
|
||||
const translated = traverseJSX((ast.body[0] as any).expression);
|
||||
const str = content.replace(matches[1] + '>', translated);
|
||||
await fs.writeFile(filename.replace('.tsd', '.ts'), str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function traverseJSX(node: any): string {
|
||||
if (node.type === 'JSXElement') {
|
||||
const tag = node.openingElement.name.name;
|
||||
const attrs: Record<string, any> = {};
|
||||
for (const attr of node.openingElement.attributes) {
|
||||
if (attr.type === 'JSXAttribute') {
|
||||
const name = attr.name.name;
|
||||
const value = attr.value;
|
||||
if (value.type === 'Literal') {
|
||||
attrs[name] = value.value;
|
||||
} else if (value.type === 'JSXExpressionContainer') {
|
||||
attrs[name] = `{${generateCode(value.expression)}}`;
|
||||
} else if (value) {
|
||||
attrs[name] = value.raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
const children = [];
|
||||
for (const child of node.children) {
|
||||
const translated = traverseJSX(child);
|
||||
if (translated) {
|
||||
children.push(translated);
|
||||
}
|
||||
}
|
||||
return `StarKitten.createElement("${tag}", ${JSON.stringify(attrs)}${children.length > 0 ? ', ' + children.join(', ') : ''})`;
|
||||
} else if (node.type === 'JSXExpressionContainer') {
|
||||
const expr = generateCode(node.expression);
|
||||
if (node.expression.type === 'TemplateLiteral' || (node.expression.type === 'Literal' && typeof node.expression.value === 'string')) {
|
||||
return `""+ ${expr} +""`;
|
||||
} else {
|
||||
return expr;
|
||||
}
|
||||
} else if (node.type === 'JSXText') {
|
||||
const text = node.value.trim();
|
||||
if (text) {
|
||||
return `"${text}"`;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function generateCode(node: any): string {
|
||||
if (node.type === 'JSXElement') {
|
||||
return traverseJSX(node);
|
||||
} else if (node.type === 'Identifier') {
|
||||
return node.name;
|
||||
} else if (node.type === 'Literal') {
|
||||
return JSON.stringify(node.value);
|
||||
} else if (node.type === 'TemplateLiteral') {
|
||||
const quasis = node.quasis.map((q: any) => q.value.raw);
|
||||
const expressions = node.expressions.map((e: any) => generateCode(e));
|
||||
let result = quasis[0];
|
||||
for (let i = 0; i < expressions.length; i++) {
|
||||
result += '${' + expressions[i] + '}' + quasis[i + 1];
|
||||
}
|
||||
return '`' + result + '`';
|
||||
} else if (node.type === 'MemberExpression') {
|
||||
const op = node.optional ? '?.' : '.';
|
||||
return generateCode(node.object) + op + (node.computed ? '[' + generateCode(node.property) + ']' : generateCode(node.property));
|
||||
} else if (node.type === 'OptionalMemberExpression') {
|
||||
return generateCode(node.object) + '?.' + (node.computed ? '[' + generateCode(node.property) + ']' : generateCode(node.property));
|
||||
} else if (node.type === 'CallExpression') {
|
||||
return generateCode(node.callee) + '(' + node.arguments.map((a: any) => generateCode(a)).join(', ') + ')';
|
||||
} else if (node.type === 'BinaryExpression') {
|
||||
return generateCode(node.left) + ' ' + node.operator + ' ' + generateCode(node.right);
|
||||
} else if (node.type === 'ConditionalExpression') {
|
||||
return generateCode(node.test) + ' ? ' + generateCode(node.consequent) + ' : ' + generateCode(node.alternate);
|
||||
} else if (node.type === 'LogicalExpression') {
|
||||
return generateCode(node.left) + ' ' + node.operator + ' ' + generateCode(node.right);
|
||||
} else if (node.type === 'UnaryExpression') {
|
||||
return node.operator + generateCode(node.argument);
|
||||
} else if (node.type === 'ObjectExpression') {
|
||||
return '{' + node.properties.map((p: any) => generateCode(p.key) + ': ' + generateCode(p.value)).join(', ') + '}';
|
||||
} else if (node.type === 'ArrayExpression') {
|
||||
return '[' + node.elements.map((e: any) => generateCode(e)).join(', ') + ']';
|
||||
} else {
|
||||
return node.raw || node.name || 'unknown';
|
||||
}
|
||||
}
|
||||
import fs from 'node:fs/promises';
|
||||
import * as acorn from 'acorn';
|
||||
import jsx from 'acorn-jsx';
|
||||
|
||||
const JSD_STRING = /\(\s*(<.*)>\s*\)/gs;
|
||||
|
||||
const parser = acorn.Parser.extend(jsx());
|
||||
|
||||
export async function parseJSDFile(filename: string) {
|
||||
const content = (await fs.readFile(filename)).toString();
|
||||
|
||||
const matches = JSD_STRING.exec(content);
|
||||
if (matches) {
|
||||
const jsxc = matches[1] + '>';
|
||||
const ast = parser.parse(jsxc, { ecmaVersion: 2020, sourceType: 'module' });
|
||||
const translated = traverseJSX((ast.body[0] as any).expression);
|
||||
const str = content.replace(matches[1] + '>', translated);
|
||||
await fs.writeFile(filename.replace('.tsd', '.ts'), str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function traverseJSX(node: any): string {
|
||||
if (node.type === 'JSXElement') {
|
||||
const tag = node.openingElement.name.name;
|
||||
const attrs: Record<string, any> = {};
|
||||
for (const attr of node.openingElement.attributes) {
|
||||
if (attr.type === 'JSXAttribute') {
|
||||
const name = attr.name.name;
|
||||
const value = attr.value;
|
||||
if (value.type === 'Literal') {
|
||||
attrs[name] = value.value;
|
||||
} else if (value.type === 'JSXExpressionContainer') {
|
||||
attrs[name] = `{${generateCode(value.expression)}}`;
|
||||
} else if (value) {
|
||||
attrs[name] = value.raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
const children = [];
|
||||
for (const child of node.children) {
|
||||
const translated = traverseJSX(child);
|
||||
if (translated) {
|
||||
children.push(translated);
|
||||
}
|
||||
}
|
||||
return `StarKitten.createElement("${tag}", ${JSON.stringify(attrs)}${children.length > 0 ? ', ' + children.join(', ') : ''})`;
|
||||
} else if (node.type === 'JSXExpressionContainer') {
|
||||
const expr = generateCode(node.expression);
|
||||
if (node.expression.type === 'TemplateLiteral' || (node.expression.type === 'Literal' && typeof node.expression.value === 'string')) {
|
||||
return `""+ ${expr} +""`;
|
||||
} else {
|
||||
return expr;
|
||||
}
|
||||
} else if (node.type === 'JSXText') {
|
||||
const text = node.value.trim();
|
||||
if (text) {
|
||||
return `"${text}"`;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function generateCode(node: any): string {
|
||||
if (node.type === 'JSXElement') {
|
||||
return traverseJSX(node);
|
||||
} else if (node.type === 'Identifier') {
|
||||
return node.name;
|
||||
} else if (node.type === 'Literal') {
|
||||
return JSON.stringify(node.value);
|
||||
} else if (node.type === 'TemplateLiteral') {
|
||||
const quasis = node.quasis.map((q: any) => q.value.raw);
|
||||
const expressions = node.expressions.map((e: any) => generateCode(e));
|
||||
let result = quasis[0];
|
||||
for (let i = 0; i < expressions.length; i++) {
|
||||
result += '${' + expressions[i] + '}' + quasis[i + 1];
|
||||
}
|
||||
return '`' + result + '`';
|
||||
} else if (node.type === 'MemberExpression') {
|
||||
const op = node.optional ? '?.' : '.';
|
||||
return generateCode(node.object) + op + (node.computed ? '[' + generateCode(node.property) + ']' : generateCode(node.property));
|
||||
} else if (node.type === 'OptionalMemberExpression') {
|
||||
return generateCode(node.object) + '?.' + (node.computed ? '[' + generateCode(node.property) + ']' : generateCode(node.property));
|
||||
} else if (node.type === 'CallExpression') {
|
||||
return generateCode(node.callee) + '(' + node.arguments.map((a: any) => generateCode(a)).join(', ') + ')';
|
||||
} else if (node.type === 'BinaryExpression') {
|
||||
return generateCode(node.left) + ' ' + node.operator + ' ' + generateCode(node.right);
|
||||
} else if (node.type === 'ConditionalExpression') {
|
||||
return generateCode(node.test) + ' ? ' + generateCode(node.consequent) + ' : ' + generateCode(node.alternate);
|
||||
} else if (node.type === 'LogicalExpression') {
|
||||
return generateCode(node.left) + ' ' + node.operator + ' ' + generateCode(node.right);
|
||||
} else if (node.type === 'UnaryExpression') {
|
||||
return node.operator + generateCode(node.argument);
|
||||
} else if (node.type === 'ObjectExpression') {
|
||||
return '{' + node.properties.map((p: any) => generateCode(p.key) + ': ' + generateCode(p.value)).join(', ') + '}';
|
||||
} else if (node.type === 'ArrayExpression') {
|
||||
return '[' + node.elements.map((e: any) => generateCode(e)).join(', ') + ']';
|
||||
} else {
|
||||
return node.raw || node.name || 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createActionRow } from '@components';
|
||||
|
||||
export function ActionRow(props: { children: any | any[] }) {
|
||||
return createActionRow(...(Array.isArray(props.children) ? props.children : [props.children]));
|
||||
}
|
||||
import { createActionRow } from '@components';
|
||||
|
||||
export function ActionRow(props: { children: any | any[] }) {
|
||||
return createActionRow(...(Array.isArray(props.children) ? props.children : [props.children]));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createButton, type ButtonStyle } from '@components';
|
||||
import type { PartialEmoji } from '@projectdysnomia/dysnomia';
|
||||
|
||||
export function Button(props: { label: string; customId: string; style?: ButtonStyle; emoji?: PartialEmoji; disabled?: boolean }) {
|
||||
return createButton(props.label, props.customId, { style: props.style, emoji: props.emoji, disabled: props.disabled });
|
||||
}
|
||||
import { createButton, type ButtonStyle } from '@components';
|
||||
import type { PartialEmoji } from '@projectdysnomia/dysnomia';
|
||||
|
||||
export function Button(props: { label: string; customId: string; style: ButtonStyle; emoji?: PartialEmoji; disabled?: boolean }) {
|
||||
return createButton(props.label, props.customId, { style: props.style, emoji: props.emoji, disabled: props.disabled });
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createContainer } from '@components';
|
||||
|
||||
export function Container(props: { accent?: number; spoiler?: boolean; children: any | any[] }) {
|
||||
return createContainer(
|
||||
{ accent_color: props.accent, spoiler: props.spoiler },
|
||||
...(Array.isArray(props.children) ? props.children : [props.children]),
|
||||
);
|
||||
}
|
||||
import { createContainer } from '@components';
|
||||
|
||||
export function Container(props: { accent?: number; spoiler?: boolean; children: any | any[] }) {
|
||||
return createContainer(
|
||||
{ accent_color: props.accent, spoiler: props.spoiler },
|
||||
...(Array.isArray(props.children) ? props.children : [props.children]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from './action-row';
|
||||
export * from './button';
|
||||
export * from './container';
|
||||
export * from './text-display';
|
||||
export * from './action-row';
|
||||
export * from './button';
|
||||
export * from './container';
|
||||
export * from './text-display';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createTextDisplay } from '@components/builders';
|
||||
|
||||
export function TextDisplay(props: { content: string }) {
|
||||
return createTextDisplay(props.content);
|
||||
}
|
||||
import { createTextDisplay } from '@components/builders';
|
||||
|
||||
export function TextDisplay(props: { content: string }) {
|
||||
return createTextDisplay(props.content);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './runtime';
|
||||
export * from './components';
|
||||
export * as JSX from './jsx';
|
||||
export * from './components';
|
||||
export * from './jsx';
|
||||
export * from './runtime';
|
||||
|
||||
1
packages/discord/src/jsx/jsx-dev-runtime.ts
Normal file
1
packages/discord/src/jsx/jsx-dev-runtime.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { jsxDEV } from './runtime';
|
||||
1
packages/discord/src/jsx/jsx-runtime.ts
Normal file
1
packages/discord/src/jsx/jsx-runtime.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { jsx } from './runtime';
|
||||
@@ -1,69 +1,69 @@
|
||||
import {
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type Component =
|
||||
| ActionRow
|
||||
| Button
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
| TextInput
|
||||
| LabelComponent
|
||||
| ContainerComponent
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent
|
||||
| InteractionButton
|
||||
| URLButton
|
||||
| PremiumButton
|
||||
| ThumbnailComponent;
|
||||
|
||||
export type Element = Component | Promise<Component>;
|
||||
|
||||
export interface ElementClass {
|
||||
render: any;
|
||||
}
|
||||
|
||||
export interface ElementAttributesProperty {
|
||||
props: {};
|
||||
}
|
||||
|
||||
export interface IntrinsicElements {
|
||||
// Allow any element, but prefer known elements
|
||||
[elemName: string]: any;
|
||||
// Known elements
|
||||
ActionRow: { children: any | any[] };
|
||||
Button: {
|
||||
label: string;
|
||||
customId: string;
|
||||
style?: number;
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
};
|
||||
Container: { color?: string; accent?: number; spoiler?: boolean; children: any | any[] };
|
||||
TextDisplay: { content: string };
|
||||
}
|
||||
import {
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type Component =
|
||||
| ActionRow
|
||||
| Button
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
| TextInput
|
||||
| LabelComponent
|
||||
| ContainerComponent
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent
|
||||
| InteractionButton
|
||||
| URLButton
|
||||
| PremiumButton
|
||||
| ThumbnailComponent;
|
||||
|
||||
export type Element = Component | Promise<Component>;
|
||||
|
||||
export interface ElementClass {
|
||||
render: any;
|
||||
}
|
||||
|
||||
export interface ElementAttributesProperty {
|
||||
props: {};
|
||||
}
|
||||
|
||||
export interface IntrinsicElements {
|
||||
// Allow any element, but prefer known elements
|
||||
// [elemName: string]: any;
|
||||
// Known elements (forcing re-parse)
|
||||
actionRow: { children: any | any[] };
|
||||
button: {
|
||||
label: string;
|
||||
customId: string;
|
||||
style: number;
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
};
|
||||
container: { color?: string; accent?: number; spoiler?: boolean; children: any | any[] };
|
||||
textDisplay: { content: string };
|
||||
}
|
||||
|
||||
@@ -1,30 +1,52 @@
|
||||
export function jsx(type: any, props: Record<string, any>) {
|
||||
console.log('JSX', type, props);
|
||||
if (typeof type === 'function') {
|
||||
return type(props);
|
||||
}
|
||||
return {
|
||||
type,
|
||||
props,
|
||||
};
|
||||
}
|
||||
|
||||
export function jsxDEV(
|
||||
type: any,
|
||||
props: Record<string, any>,
|
||||
key: string | number | symbol,
|
||||
isStaticChildren: boolean,
|
||||
source: any,
|
||||
self: any,
|
||||
) {
|
||||
console.log('JSX DEV', type, props);
|
||||
if (typeof type === 'function') {
|
||||
return type(props);
|
||||
}
|
||||
return {
|
||||
type,
|
||||
props: { ...props, key },
|
||||
_source: source,
|
||||
_self: self,
|
||||
};
|
||||
}
|
||||
import { ActionRow } from './components/action-row';
|
||||
import { Button } from './components/button';
|
||||
import { Container } from './components/container';
|
||||
import { TextDisplay } from './components/text-display';
|
||||
|
||||
const intrinsicComponentMap: Record<string, (props: any) => any> = {
|
||||
actionRow: ActionRow,
|
||||
button: Button,
|
||||
container: Container,
|
||||
textDisplay: TextDisplay,
|
||||
};
|
||||
|
||||
export function jsx(type: any, props: Record<string, any>) {
|
||||
console.log('JSX', type, props);
|
||||
if (typeof type === 'function') {
|
||||
return type(props);
|
||||
}
|
||||
|
||||
if (typeof type === 'string' && intrinsicComponentMap[type]) {
|
||||
return intrinsicComponentMap[type](props);
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
props,
|
||||
};
|
||||
}
|
||||
|
||||
export function jsxDEV(
|
||||
type: any,
|
||||
props: Record<string, any>,
|
||||
key: string | number | symbol,
|
||||
isStaticChildren: boolean,
|
||||
source: any,
|
||||
self: any,
|
||||
) {
|
||||
console.log('JSX DEV', type, props);
|
||||
if (typeof type === 'function') {
|
||||
return type(props);
|
||||
}
|
||||
|
||||
if (typeof type === 'string' && intrinsicComponentMap[type]) {
|
||||
return intrinsicComponentMap[type](props);
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
props: { ...props, key },
|
||||
_source: source,
|
||||
_self: self,
|
||||
};
|
||||
}
|
||||
|
||||
16
packages/discord/src/jsx/types.d.ts
vendored
16
packages/discord/src/jsx/types.d.ts
vendored
@@ -1,8 +1,8 @@
|
||||
import type { Component, IntrinsicElements as StarKittenIntrinsicElements } from './jsx';
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
type Element = Component;
|
||||
interface IntrinsicElements extends StarKittenIntrinsicElements {}
|
||||
}
|
||||
}
|
||||
import type { Component, IntrinsicElements as StarKittenIntrinsicElements } from './jsx';
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
type Element = Component;
|
||||
interface IntrinsicElements extends StarKittenIntrinsicElements {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
export type Locales = 'en' | 'ru' | 'de' | 'fr' | 'ja' | 'es' | 'zh' | 'ko';
|
||||
export const ALL_LOCALES: Locales[] = ['en', 'ru', 'de', 'fr', 'ja', 'es', 'zh', 'ko'];
|
||||
export const DEFAULT_LOCALE: Locales = 'en';
|
||||
export const LOCALE_NAMES: { [key in Locales]: string } = {
|
||||
en: 'English',
|
||||
ru: 'Русский',
|
||||
de: 'Deutsch',
|
||||
fr: 'Français',
|
||||
ja: '日本語',
|
||||
es: 'Español',
|
||||
zh: '中文',
|
||||
ko: '한국어',
|
||||
};
|
||||
export function toDiscordLocale(locale: Locales): string {
|
||||
switch (locale) {
|
||||
case 'en': return 'en-US';
|
||||
case 'ru': return 'ru';
|
||||
case 'de': return 'de';
|
||||
case 'fr': return 'fr';
|
||||
case 'ja': return 'ja';
|
||||
case 'es': return 'es-ES';
|
||||
case 'zh': return 'zh-CN';
|
||||
case 'ko': return 'ko';
|
||||
default: return 'en-US';
|
||||
}
|
||||
export type Locales = 'en' | 'ru' | 'de' | 'fr' | 'ja' | 'es' | 'zh' | 'ko';
|
||||
export const ALL_LOCALES: Locales[] = ['en', 'ru', 'de', 'fr', 'ja', 'es', 'zh', 'ko'];
|
||||
export const DEFAULT_LOCALE: Locales = 'en';
|
||||
export const LOCALE_NAMES: { [key in Locales]: string } = {
|
||||
en: 'English',
|
||||
ru: 'Русский',
|
||||
de: 'Deutsch',
|
||||
fr: 'Français',
|
||||
ja: '日本語',
|
||||
es: 'Español',
|
||||
zh: '中文',
|
||||
ko: '한국어',
|
||||
};
|
||||
export function toDiscordLocale(locale: Locales): string {
|
||||
switch (locale) {
|
||||
case 'en': return 'en-US';
|
||||
case 'ru': return 'ru';
|
||||
case 'de': return 'de';
|
||||
case 'fr': return 'fr';
|
||||
case 'ja': return 'ja';
|
||||
case 'es': return 'es-ES';
|
||||
case 'zh': return 'zh-CN';
|
||||
case 'ko': return 'ko';
|
||||
default: return 'en-US';
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './pages';
|
||||
export * from './subroutes';
|
||||
export * from './pages';
|
||||
export * from './subroutes';
|
||||
|
||||
@@ -1,166 +1,166 @@
|
||||
import { isAutocomplete, isMessageComponent, isModalSubmit, isPing, type CommandContext } from '@commands';
|
||||
import {
|
||||
Constants,
|
||||
type InteractionContentEdit,
|
||||
type InteractionModalContent,
|
||||
type CommandInteraction,
|
||||
type ComponentInteraction,
|
||||
type ModalSubmitInteraction,
|
||||
Interaction,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type PagesInteraction = CommandInteraction | ModalSubmitInteraction | ComponentInteraction;
|
||||
export enum PageType {
|
||||
MODAL = 'modal',
|
||||
MESSAGE = 'message',
|
||||
FOLLOWUP = 'followup',
|
||||
}
|
||||
|
||||
export interface Page<T> {
|
||||
key: string;
|
||||
type?: PageType; // defaults to MESSAGE
|
||||
followUpFlags?: number;
|
||||
render: (
|
||||
ctx: PageContext<T>,
|
||||
) => (InteractionModalContent | InteractionContentEdit) | Promise<InteractionModalContent | InteractionContentEdit>;
|
||||
}
|
||||
|
||||
export interface PagesOptions<T> {
|
||||
pages: Record<string, Page<T>>;
|
||||
initialPage?: string;
|
||||
timeout?: number; // in seconds
|
||||
ephemeral?: boolean; // whether the initial message should be ephemeral
|
||||
useEmbeds?: boolean; // will not enable components v2
|
||||
initialStateData?: T; // initial state to merge with default state
|
||||
router?: (ctx: PageContext<T>) => string; // function to determine the next page key
|
||||
}
|
||||
|
||||
export interface PageState<T> {
|
||||
currentPage: string;
|
||||
timeoutAt: number; // timestamp in ms
|
||||
lastInteractionAt?: number; // timestamp in ms
|
||||
messageId?: string;
|
||||
channelId?: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface PageContext<T> {
|
||||
state: PageState<T>;
|
||||
custom_id: string; // current interaction custom_id
|
||||
interaction: PagesInteraction;
|
||||
goToPage: (pageKey: string) => Promise<InteractionContentEdit>;
|
||||
}
|
||||
|
||||
function createPageContext<T>(interaction: PagesInteraction, options: PagesOptions<T>, state: PageState<T>): PageContext<T> {
|
||||
return {
|
||||
state,
|
||||
interaction,
|
||||
custom_id: 'custom_id' in interaction.data ? interaction.data.custom_id : (options.initialPage ?? 'root'),
|
||||
goToPage: (pageKey: string) => {
|
||||
const page = options.pages[pageKey];
|
||||
this.state.currentPage = pageKey;
|
||||
if (!page) {
|
||||
throw new Error(`Page with key "${pageKey}" not found`);
|
||||
}
|
||||
return page.render(createPageContext(interaction, options, { ...state, currentPage: pageKey })) as Promise<InteractionContentEdit>;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function defaultPageState<T>(options: PagesOptions<T>): PageState<T> {
|
||||
const timeoutAt = options.timeout ? Date.now() + options.timeout * 1000 : Infinity;
|
||||
return {
|
||||
currentPage: options.initialPage ?? options.pages[0].key,
|
||||
timeoutAt,
|
||||
lastInteractionAt: Date.now(),
|
||||
data: options.initialStateData ?? ({} as T),
|
||||
};
|
||||
}
|
||||
|
||||
function getPageState<T>(options: PagesOptions<T>, cmdCtx: CommandContext & { state: { __pageState?: PageState<T> } }) {
|
||||
const cmdState = cmdCtx.state;
|
||||
if ('__pageState' in cmdState && cmdState.__pageState) {
|
||||
return cmdState.__pageState as PageState<T>;
|
||||
}
|
||||
cmdState.__pageState = defaultPageState(options);
|
||||
return cmdState.__pageState as PageState<T>;
|
||||
}
|
||||
|
||||
function validateOptions<T>(options: PagesOptions<T>) {
|
||||
const keys = Object.keys(options.pages);
|
||||
const uniqueKeys = new Set(keys);
|
||||
if (uniqueKeys.size !== keys.length) {
|
||||
throw new Error('Duplicate page keys found');
|
||||
}
|
||||
}
|
||||
|
||||
function getFlags(options: PagesOptions<any>) {
|
||||
let flags = 0;
|
||||
if (options.ephemeral) {
|
||||
flags |= Constants.MessageFlags.EPHEMERAL;
|
||||
}
|
||||
if (!options.useEmbeds) {
|
||||
flags |= Constants.MessageFlags.IS_COMPONENTS_V2;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
export async function usePages<T>(options: PagesOptions<T>, interaction: Interaction, cmdCtx: CommandContext) {
|
||||
if (isAutocomplete(interaction) || isPing(interaction)) {
|
||||
throw new Error('usePages cannot be used with autocomplete or ping interactions');
|
||||
}
|
||||
const pagesInteraction = interaction as PagesInteraction;
|
||||
validateOptions(options);
|
||||
const pageState = getPageState(options, cmdCtx);
|
||||
const pageContext = createPageContext(pagesInteraction, options, pageState);
|
||||
const pageKey =
|
||||
options.router ? options.router(pageContext) : (pageContext.custom_id ?? options.initialPage ?? Object.keys(options.pages)[0]);
|
||||
// if we have subroutes, we only want the main route from the page key
|
||||
const page = options.pages[pageKey.split(':')[0]] ?? options.pages[0];
|
||||
pageContext.state.currentPage = page.key;
|
||||
|
||||
if (page.type === PageType.MODAL && !isModalSubmit(pagesInteraction)) {
|
||||
// we don't defer modals and can't respond to a modal with a modal.
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
return await pagesInteraction.createModal(content as InteractionModalContent);
|
||||
}
|
||||
|
||||
if (page.type === PageType.FOLLOWUP) {
|
||||
if (!pageState.messageId) {
|
||||
throw new Error('Cannot send a followup message before an initial message has been sent');
|
||||
}
|
||||
const flags = page.type === PageType.FOLLOWUP ? (page.followUpFlags ?? getFlags(options)) : getFlags(options);
|
||||
await pagesInteraction.defer(flags);
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
return await pagesInteraction.createFollowup({
|
||||
flags,
|
||||
...(content as InteractionContentEdit),
|
||||
});
|
||||
}
|
||||
|
||||
if (pageState.messageId && isMessageComponent(pagesInteraction)) {
|
||||
await pagesInteraction.deferUpdate();
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
return await pagesInteraction.editMessage(pageState.messageId, content as InteractionContentEdit);
|
||||
}
|
||||
|
||||
{
|
||||
await pagesInteraction.defer(getFlags(options));
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
const message = await pagesInteraction.createFollowup({
|
||||
flags: getFlags(options),
|
||||
...(content as InteractionContentEdit),
|
||||
});
|
||||
pageState.messageId = message.id;
|
||||
pageState.channelId = message.channel?.id;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
function isPromise<T>(value: T | Promise<T>): value is Promise<T> {
|
||||
return typeof (value as Promise<T>)?.then === 'function';
|
||||
}
|
||||
import { isAutocomplete, isMessageComponent, isModalSubmit, isPing, type CommandContext } from '@commands';
|
||||
import {
|
||||
Constants,
|
||||
type InteractionContentEdit,
|
||||
type InteractionModalContent,
|
||||
type CommandInteraction,
|
||||
type ComponentInteraction,
|
||||
type ModalSubmitInteraction,
|
||||
Interaction,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type PagesInteraction = CommandInteraction | ModalSubmitInteraction | ComponentInteraction;
|
||||
export enum PageType {
|
||||
MODAL = 'modal',
|
||||
MESSAGE = 'message',
|
||||
FOLLOWUP = 'followup',
|
||||
}
|
||||
|
||||
export interface Page<T> {
|
||||
key: string;
|
||||
type?: PageType; // defaults to MESSAGE
|
||||
followUpFlags?: number;
|
||||
render: (
|
||||
ctx: PageContext<T>,
|
||||
) => (InteractionModalContent | InteractionContentEdit) | Promise<InteractionModalContent | InteractionContentEdit>;
|
||||
}
|
||||
|
||||
export interface PagesOptions<T> {
|
||||
pages: Record<string, Page<T>>;
|
||||
initialPage?: string;
|
||||
timeout?: number; // in seconds
|
||||
ephemeral?: boolean; // whether the initial message should be ephemeral
|
||||
useEmbeds?: boolean; // will not enable components v2
|
||||
initialStateData?: T; // initial state to merge with default state
|
||||
router?: (ctx: PageContext<T>) => string; // function to determine the next page key
|
||||
}
|
||||
|
||||
export interface PageState<T> {
|
||||
currentPage: string;
|
||||
timeoutAt: number; // timestamp in ms
|
||||
lastInteractionAt?: number; // timestamp in ms
|
||||
messageId?: string;
|
||||
channelId?: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface PageContext<T> {
|
||||
state: PageState<T>;
|
||||
custom_id: string; // current interaction custom_id
|
||||
interaction: PagesInteraction;
|
||||
goToPage: (pageKey: string) => Promise<InteractionContentEdit>;
|
||||
}
|
||||
|
||||
function createPageContext<T>(interaction: PagesInteraction, options: PagesOptions<T>, state: PageState<T>): PageContext<T> {
|
||||
return {
|
||||
state,
|
||||
interaction,
|
||||
custom_id: 'custom_id' in interaction.data ? interaction.data.custom_id : (options.initialPage ?? 'root'),
|
||||
goToPage: (pageKey: string) => {
|
||||
const page = options.pages[pageKey];
|
||||
this.state.currentPage = pageKey;
|
||||
if (!page) {
|
||||
throw new Error(`Page with key "${pageKey}" not found`);
|
||||
}
|
||||
return page.render(createPageContext(interaction, options, { ...state, currentPage: pageKey })) as Promise<InteractionContentEdit>;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function defaultPageState<T>(options: PagesOptions<T>): PageState<T> {
|
||||
const timeoutAt = options.timeout ? Date.now() + options.timeout * 1000 : Infinity;
|
||||
return {
|
||||
currentPage: options.initialPage ?? options.pages[0].key,
|
||||
timeoutAt,
|
||||
lastInteractionAt: Date.now(),
|
||||
data: options.initialStateData ?? ({} as T),
|
||||
};
|
||||
}
|
||||
|
||||
function getPageState<T>(options: PagesOptions<T>, cmdCtx: CommandContext & { state: { __pageState?: PageState<T> } }) {
|
||||
const cmdState = cmdCtx.state;
|
||||
if ('__pageState' in cmdState && cmdState.__pageState) {
|
||||
return cmdState.__pageState as PageState<T>;
|
||||
}
|
||||
cmdState.__pageState = defaultPageState(options);
|
||||
return cmdState.__pageState as PageState<T>;
|
||||
}
|
||||
|
||||
function validateOptions<T>(options: PagesOptions<T>) {
|
||||
const keys = Object.keys(options.pages);
|
||||
const uniqueKeys = new Set(keys);
|
||||
if (uniqueKeys.size !== keys.length) {
|
||||
throw new Error('Duplicate page keys found');
|
||||
}
|
||||
}
|
||||
|
||||
function getFlags(options: PagesOptions<any>) {
|
||||
let flags = 0;
|
||||
if (options.ephemeral) {
|
||||
flags |= Constants.MessageFlags.EPHEMERAL;
|
||||
}
|
||||
if (!options.useEmbeds) {
|
||||
flags |= Constants.MessageFlags.IS_COMPONENTS_V2;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
export async function usePages<T>(options: PagesOptions<T>, interaction: Interaction, cmdCtx: CommandContext) {
|
||||
if (isAutocomplete(interaction) || isPing(interaction)) {
|
||||
throw new Error('usePages cannot be used with autocomplete or ping interactions');
|
||||
}
|
||||
const pagesInteraction = interaction as PagesInteraction;
|
||||
validateOptions(options);
|
||||
const pageState = getPageState(options, cmdCtx);
|
||||
const pageContext = createPageContext(pagesInteraction, options, pageState);
|
||||
const pageKey =
|
||||
options.router ? options.router(pageContext) : (pageContext.custom_id ?? options.initialPage ?? Object.keys(options.pages)[0]);
|
||||
// if we have subroutes, we only want the main route from the page key
|
||||
const page = options.pages[pageKey.split(':')[0]] ?? options.pages[0];
|
||||
pageContext.state.currentPage = page.key;
|
||||
|
||||
if (page.type === PageType.MODAL && !isModalSubmit(pagesInteraction)) {
|
||||
// we don't defer modals and can't respond to a modal with a modal.
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
return await pagesInteraction.createModal(content as InteractionModalContent);
|
||||
}
|
||||
|
||||
if (page.type === PageType.FOLLOWUP) {
|
||||
if (!pageState.messageId) {
|
||||
throw new Error('Cannot send a followup message before an initial message has been sent');
|
||||
}
|
||||
const flags = page.type === PageType.FOLLOWUP ? (page.followUpFlags ?? getFlags(options)) : getFlags(options);
|
||||
await pagesInteraction.defer(flags);
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
return await pagesInteraction.createFollowup({
|
||||
flags,
|
||||
...(content as InteractionContentEdit),
|
||||
});
|
||||
}
|
||||
|
||||
if (pageState.messageId && isMessageComponent(pagesInteraction)) {
|
||||
await pagesInteraction.deferUpdate();
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
return await pagesInteraction.editMessage(pageState.messageId, content as InteractionContentEdit);
|
||||
}
|
||||
|
||||
{
|
||||
await pagesInteraction.defer(getFlags(options));
|
||||
const cnt = page.render(pageContext);
|
||||
const content = isPromise(cnt) ? await cnt : cnt;
|
||||
const message = await pagesInteraction.createFollowup({
|
||||
flags: getFlags(options),
|
||||
...(content as InteractionContentEdit),
|
||||
});
|
||||
pageState.messageId = message.id;
|
||||
pageState.channelId = message.channel?.id;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
function isPromise<T>(value: T | Promise<T>): value is Promise<T> {
|
||||
return typeof (value as Promise<T>)?.then === 'function';
|
||||
}
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
import type { PartialEmoji } from '@projectdysnomia/dysnomia';
|
||||
import { createActionRow, createButton, createMediaGallery, type ButtonOptions, type ContainerItems } from '@components';
|
||||
import type { PageContext } from './pages';
|
||||
|
||||
export function getSubrouteKey(prefix: string, subroutes: string[]) {
|
||||
return `${prefix}:${subroutes.join(':')}`;
|
||||
}
|
||||
|
||||
export function parseSubrouteKey(key: string, expectedPrefix: string, expectedLength: number, defaults: string[] = []) {
|
||||
const parts = key.split(':');
|
||||
if (parts[0] !== expectedPrefix) {
|
||||
throw new Error(`Unexpected prefix: ${parts[0]}`);
|
||||
}
|
||||
if (parts.length - 1 < expectedLength && defaults.length) {
|
||||
// fill in defaults
|
||||
parts.push(...defaults.slice(parts.length - 1));
|
||||
}
|
||||
if (parts.length !== expectedLength + 1) {
|
||||
throw new Error(`Expected ${expectedLength} subroutes, but got ${parts.length - 1}`);
|
||||
}
|
||||
return parts.slice(1);
|
||||
}
|
||||
|
||||
export function renderSubrouteButtons(
|
||||
currentSubroute: string,
|
||||
subRoutes: string[],
|
||||
subrouteIndex: number,
|
||||
prefix: string,
|
||||
subroutes: { label: string; value: string; emoji?: PartialEmoji }[],
|
||||
options?: Partial<ButtonOptions>,
|
||||
) {
|
||||
return subroutes
|
||||
.filter((sr) => sr !== undefined)
|
||||
.map(({ label, value, emoji }) => {
|
||||
const routes = [...subRoutes];
|
||||
routes[subrouteIndex] = currentSubroute == value ? '_' : value;
|
||||
return createButton(label, getSubrouteKey(prefix, routes), {
|
||||
...options,
|
||||
disabled: value === currentSubroute,
|
||||
emoji,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export interface SubrouteOptions {
|
||||
label: string;
|
||||
value: string;
|
||||
emoji?: PartialEmoji;
|
||||
}
|
||||
|
||||
export function renderSubroutes<T, CType = ContainerItems>(
|
||||
context: PageContext<T>,
|
||||
prefix: string,
|
||||
subroutes: (SubrouteOptions & {
|
||||
banner?: string;
|
||||
actionRowPosition?: 'top' | 'bottom';
|
||||
})[][],
|
||||
render: (currentSubroute: string, ctx: PageContext<T>) => CType,
|
||||
btnOptions?: Partial<ButtonOptions>,
|
||||
defaultSubroutes?: string[], // if not provided, will use the first option of each subroute
|
||||
): CType[] {
|
||||
const currentSubroutes = parseSubrouteKey(
|
||||
context.custom_id,
|
||||
prefix,
|
||||
subroutes.length,
|
||||
defaultSubroutes || subroutes.map((s) => s[0].value),
|
||||
);
|
||||
|
||||
const components = subroutes
|
||||
.filter((sr) => sr.length > 0)
|
||||
.map((srOpts, index) => {
|
||||
const opts = srOpts.filter((sr) => sr !== undefined);
|
||||
if (opts.length === 0) return undefined;
|
||||
// find the current subroute, or default to the first
|
||||
const sri = opts.findIndex((s) => s.value === currentSubroutes[index]);
|
||||
const current = opts[sri] || opts[0];
|
||||
const components = [];
|
||||
|
||||
const actionRow = createActionRow(...renderSubrouteButtons(current.value, currentSubroutes, index, prefix, opts, btnOptions));
|
||||
|
||||
if (current.banner) {
|
||||
components.push(createMediaGallery({ url: current.banner }));
|
||||
}
|
||||
|
||||
if (!current.actionRowPosition || current.actionRowPosition === 'top') {
|
||||
components.push(actionRow);
|
||||
}
|
||||
|
||||
components.push(render(current.value, context));
|
||||
|
||||
if (current.actionRowPosition === 'bottom') {
|
||||
components.push(actionRow);
|
||||
}
|
||||
return components;
|
||||
})
|
||||
.flat()
|
||||
.filter((c) => c !== undefined);
|
||||
return components;
|
||||
}
|
||||
import type { PartialEmoji } from '@projectdysnomia/dysnomia';
|
||||
import { createActionRow, createButton, createMediaGallery, type ButtonOptions, type ContainerItems } from '@components';
|
||||
import type { PageContext } from './pages';
|
||||
|
||||
export function getSubrouteKey(prefix: string, subroutes: string[]) {
|
||||
return `${prefix}:${subroutes.join(':')}`;
|
||||
}
|
||||
|
||||
export function parseSubrouteKey(key: string, expectedPrefix: string, expectedLength: number, defaults: string[] = []) {
|
||||
const parts = key.split(':');
|
||||
if (parts[0] !== expectedPrefix) {
|
||||
throw new Error(`Unexpected prefix: ${parts[0]}`);
|
||||
}
|
||||
if (parts.length - 1 < expectedLength && defaults.length) {
|
||||
// fill in defaults
|
||||
parts.push(...defaults.slice(parts.length - 1));
|
||||
}
|
||||
if (parts.length !== expectedLength + 1) {
|
||||
throw new Error(`Expected ${expectedLength} subroutes, but got ${parts.length - 1}`);
|
||||
}
|
||||
return parts.slice(1);
|
||||
}
|
||||
|
||||
export function renderSubrouteButtons(
|
||||
currentSubroute: string,
|
||||
subRoutes: string[],
|
||||
subrouteIndex: number,
|
||||
prefix: string,
|
||||
subroutes: { label: string; value: string; emoji?: PartialEmoji }[],
|
||||
options?: Partial<ButtonOptions>,
|
||||
) {
|
||||
return subroutes
|
||||
.filter((sr) => sr !== undefined)
|
||||
.map(({ label, value, emoji }) => {
|
||||
const routes = [...subRoutes];
|
||||
routes[subrouteIndex] = currentSubroute == value ? '_' : value;
|
||||
return createButton(label, getSubrouteKey(prefix, routes), {
|
||||
...options,
|
||||
disabled: value === currentSubroute,
|
||||
emoji,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export interface SubrouteOptions {
|
||||
label: string;
|
||||
value: string;
|
||||
emoji?: PartialEmoji;
|
||||
}
|
||||
|
||||
export function renderSubroutes<T, CType = ContainerItems>(
|
||||
context: PageContext<T>,
|
||||
prefix: string,
|
||||
subroutes: (SubrouteOptions & {
|
||||
banner?: string;
|
||||
actionRowPosition?: 'top' | 'bottom';
|
||||
})[][],
|
||||
render: (currentSubroute: string, ctx: PageContext<T>) => CType,
|
||||
btnOptions?: Partial<ButtonOptions>,
|
||||
defaultSubroutes?: string[], // if not provided, will use the first option of each subroute
|
||||
): CType[] {
|
||||
const currentSubroutes = parseSubrouteKey(
|
||||
context.custom_id,
|
||||
prefix,
|
||||
subroutes.length,
|
||||
defaultSubroutes || subroutes.map((s) => s[0].value),
|
||||
);
|
||||
|
||||
const components = subroutes
|
||||
.filter((sr) => sr.length > 0)
|
||||
.map((srOpts, index) => {
|
||||
const opts = srOpts.filter((sr) => sr !== undefined);
|
||||
if (opts.length === 0) return undefined;
|
||||
// find the current subroute, or default to the first
|
||||
const sri = opts.findIndex((s) => s.value === currentSubroutes[index]);
|
||||
const current = opts[sri] || opts[0];
|
||||
const components = [];
|
||||
|
||||
const actionRow = createActionRow(...renderSubrouteButtons(current.value, currentSubroutes, index, prefix, opts, btnOptions));
|
||||
|
||||
if (current.banner) {
|
||||
components.push(createMediaGallery({ url: current.banner }));
|
||||
}
|
||||
|
||||
if (!current.actionRowPosition || current.actionRowPosition === 'top') {
|
||||
components.push(actionRow);
|
||||
}
|
||||
|
||||
components.push(render(current.value, context));
|
||||
|
||||
if (current.actionRowPosition === 'bottom') {
|
||||
components.push(actionRow);
|
||||
}
|
||||
return components;
|
||||
})
|
||||
.flat()
|
||||
.filter((c) => c !== undefined);
|
||||
return components;
|
||||
}
|
||||
|
||||
@@ -1,40 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"composite": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@star-kitten/discord",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": false,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
|
||||
// Paths
|
||||
"paths": {
|
||||
"@*": ["./src/*"],
|
||||
"@types": ["./types/*"]
|
||||
},
|
||||
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
||||
"typeRoots": ["src/types", "./node_modules/@types"]
|
||||
"typeRoots": ["src/types", "./node_modules/@types"],
|
||||
"types": []
|
||||
},
|
||||
"include": ["src", "types", "src/jsx/types.d.ts"],
|
||||
"exclude": ["node_modules", "dist", "build", "**/*.test.ts"]
|
||||
|
||||
@@ -9,6 +9,8 @@ export default defineConfig([
|
||||
'./src/pages/index.ts',
|
||||
'./src/common/index.ts',
|
||||
'./src/jsx/index.ts',
|
||||
'./src/jsx/jsx-runtime.ts',
|
||||
'./src/jsx/jsx-dev-runtime.ts',
|
||||
],
|
||||
platform: 'node',
|
||||
dts: true,
|
||||
|
||||
130
packages/discord/types/index.d.ts
vendored
130
packages/discord/types/index.d.ts
vendored
@@ -1,65 +1,65 @@
|
||||
import {
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
declare namespace JSX {
|
||||
type Component =
|
||||
| Button
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
| TextInput
|
||||
| LabelComponent
|
||||
| ContainerComponent
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent
|
||||
| InteractionButton
|
||||
| URLButton
|
||||
| PremiumButton
|
||||
| ThumbnailComponent;
|
||||
|
||||
type Element = Component | Promise<Component>;
|
||||
|
||||
interface ElementClass {
|
||||
render: any;
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props: {};
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// Allow any element, but prefer known elements
|
||||
[elemName: string]: any;
|
||||
// Known elements
|
||||
ActionRow: { children: any | any[] };
|
||||
Button: { label: string; customId: string; style?: number; emoji?: PartialEmoji; disabled?: boolean };
|
||||
Container: { accent?: number; spoiler?: boolean; children: any | any[] };
|
||||
TextDisplay: { content: string };
|
||||
}
|
||||
}
|
||||
import {
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
declare namespace JSX {
|
||||
type Component =
|
||||
| Button
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
| TextInput
|
||||
| LabelComponent
|
||||
| ContainerComponent
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent
|
||||
| InteractionButton
|
||||
| URLButton
|
||||
| PremiumButton
|
||||
| ThumbnailComponent;
|
||||
|
||||
type Element = Component | Promise<Component>;
|
||||
|
||||
interface ElementClass {
|
||||
render: any;
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props: {};
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// Allow any element, but prefer known elements
|
||||
[elemName: string]: any;
|
||||
// Known elements
|
||||
ActionRow: { children: any | any[] };
|
||||
Button: { label: string; customId: string; style?: number; emoji?: PartialEmoji; disabled?: boolean };
|
||||
Container: { accent?: number; spoiler?: boolean; children: any | any[] };
|
||||
TextDisplay: { content: string };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"@projectdysnomia/dysnomia": "github:projectdysnomia/dysnomia#dev",
|
||||
"@star-kitten/discord": "workspace:^0.0.0",
|
||||
"@star-kitten/eve": "workspace:^0.0.0",
|
||||
"@star-kitten/util": "workspace:^0.0.0"
|
||||
"@star-kitten/util": "workspace:^0.0.0",
|
||||
"mkdirp": "^3.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bunx dotenvx run -f .env.development -- bun run --watch src/main.ts",
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
import { Constants, type ChatInputApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
import { appraiseItems, type Appraisal } from '@star-kitten/eve/third-party/janice.js';
|
||||
import { isModalSubmit } from '@star-kitten/discord/commands';
|
||||
import { componentHasIdPrefix, isModalLabel, isModalSelect, isModalTextInput } from '@star-kitten/discord/components';
|
||||
import type { CommandContext, ExecutableInteraction } from '@star-kitten/discord/commands';
|
||||
import { PageType, usePages } from '@star-kitten/discord/pages';
|
||||
import { renderAppraisal } from './renderAppraisal';
|
||||
import { renderAppraisalModal } from './renderAppraisalModal';
|
||||
|
||||
const definition: ChatInputApplicationCommandStructure = {
|
||||
type: Constants.ApplicationCommandTypes.CHAT_INPUT,
|
||||
name: 'appraise',
|
||||
nameLocalizations: {
|
||||
de: 'bewerten',
|
||||
'es-ES': 'tasar',
|
||||
fr: 'estimer',
|
||||
ja: '査定',
|
||||
ko: '감정',
|
||||
ru: 'оценить',
|
||||
'zh-CN': '评估',
|
||||
},
|
||||
description: 'Evaluate the worth of your space junk',
|
||||
descriptionLocalizations: {
|
||||
de: 'Bewerten Sie den Wert Ihres Weltraumschrotts',
|
||||
'es-ES': 'Evalúa el valor de tu chatarra espacial',
|
||||
fr: 'Évaluez la valeur de vos déchets spatiaux',
|
||||
ja: 'あなたの宇宙のガラクタの価値を評価します',
|
||||
ko: '우주 쓰레기의 가치를 평가하십시오',
|
||||
ru: 'Оцените стоимость вашего космического мусора',
|
||||
'zh-CN': '评估您宇宙垃圾的价值',
|
||||
},
|
||||
};
|
||||
|
||||
export interface AppraisalState {
|
||||
appraisal?: Appraisal;
|
||||
}
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
return await usePages<AppraisalState>(
|
||||
{
|
||||
pages: {
|
||||
appraiseModal: {
|
||||
key: 'appraiseModal',
|
||||
type: PageType.MODAL,
|
||||
render: async () => renderAppraisalModal(interaction),
|
||||
},
|
||||
appraisalResult: {
|
||||
key: 'appraisalResult',
|
||||
render: async (pageCtx) => {
|
||||
if (!isModalSubmit(interaction)) {
|
||||
throw new Error('Expected a modal submit interaction for appraisalResult page');
|
||||
}
|
||||
let marketId = 2; // Default to Jita
|
||||
let items = '';
|
||||
|
||||
interaction.data.components.forEach((comp) => {
|
||||
if (isModalLabel(comp)) {
|
||||
if (isModalSelect(comp.component) && componentHasIdPrefix(comp.component, `market`)) {
|
||||
marketId = Number.parseInt(comp.component.values[0]) || marketId;
|
||||
} else if (isModalTextInput(comp.component) && componentHasIdPrefix(comp.component, `input`)) {
|
||||
items = comp.component.value || items;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const appraisal = await appraiseItems(items, marketId);
|
||||
pageCtx.state.data.appraisal = appraisal;
|
||||
return renderAppraisal(appraisal, pageCtx, interaction);
|
||||
},
|
||||
},
|
||||
share: {
|
||||
key: 'share',
|
||||
type: PageType.FOLLOWUP,
|
||||
followUpFlags: Constants.MessageFlags.IS_COMPONENTS_V2,
|
||||
render: async (pageCtx) => renderAppraisal(pageCtx.state.data.appraisal!, pageCtx, interaction),
|
||||
},
|
||||
},
|
||||
initialPage: 'appraiseModal',
|
||||
timeout: 300, // 5 minutes
|
||||
ephemeral: true,
|
||||
},
|
||||
interaction,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
definition,
|
||||
execute,
|
||||
};
|
||||
import { Constants, type ChatInputApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
import { appraiseItems, type Appraisal } from '@star-kitten/eve/third-party/janice.js';
|
||||
import { isModalSubmit } from '@star-kitten/discord/commands';
|
||||
import { componentHasIdPrefix, isModalLabel, isModalSelect, isModalTextInput } from '@star-kitten/discord/components';
|
||||
import type { CommandContext, ExecutableInteraction } from '@star-kitten/discord/commands';
|
||||
import { PageType, usePages } from '@star-kitten/discord/pages';
|
||||
import { renderAppraisal } from './renderAppraisal';
|
||||
import { renderAppraisalModal } from './renderAppraisalModal';
|
||||
|
||||
const definition: ChatInputApplicationCommandStructure = {
|
||||
type: Constants.ApplicationCommandTypes.CHAT_INPUT,
|
||||
name: 'appraise',
|
||||
nameLocalizations: {
|
||||
de: 'bewerten',
|
||||
'es-ES': 'tasar',
|
||||
fr: 'estimer',
|
||||
ja: '査定',
|
||||
ko: '감정',
|
||||
ru: 'оценить',
|
||||
'zh-CN': '评估',
|
||||
},
|
||||
description: 'Evaluate the worth of your space junk',
|
||||
descriptionLocalizations: {
|
||||
de: 'Bewerten Sie den Wert Ihres Weltraumschrotts',
|
||||
'es-ES': 'Evalúa el valor de tu chatarra espacial',
|
||||
fr: 'Évaluez la valeur de vos déchets spatiaux',
|
||||
ja: 'あなたの宇宙のガラクタの価値を評価します',
|
||||
ko: '우주 쓰레기의 가치를 평가하십시오',
|
||||
ru: 'Оцените стоимость вашего космического мусора',
|
||||
'zh-CN': '评估您宇宙垃圾的价值',
|
||||
},
|
||||
};
|
||||
|
||||
export interface AppraisalState {
|
||||
appraisal?: Appraisal;
|
||||
}
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
return await usePages<AppraisalState>(
|
||||
{
|
||||
pages: {
|
||||
appraiseModal: {
|
||||
key: 'appraiseModal',
|
||||
type: PageType.MODAL,
|
||||
render: async () => renderAppraisalModal(interaction),
|
||||
},
|
||||
appraisalResult: {
|
||||
key: 'appraisalResult',
|
||||
render: async (pageCtx) => {
|
||||
if (!isModalSubmit(interaction)) {
|
||||
throw new Error('Expected a modal submit interaction for appraisalResult page');
|
||||
}
|
||||
let marketId = 2; // Default to Jita
|
||||
let items = '';
|
||||
|
||||
interaction.data.components.forEach((comp) => {
|
||||
if (isModalLabel(comp)) {
|
||||
if (isModalSelect(comp.component) && componentHasIdPrefix(comp.component, `market`)) {
|
||||
marketId = Number.parseInt(comp.component.values[0]) || marketId;
|
||||
} else if (isModalTextInput(comp.component) && componentHasIdPrefix(comp.component, `input`)) {
|
||||
items = comp.component.value || items;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const appraisal = await appraiseItems(items, marketId);
|
||||
pageCtx.state.data.appraisal = appraisal;
|
||||
return renderAppraisal(appraisal, pageCtx, interaction);
|
||||
},
|
||||
},
|
||||
share: {
|
||||
key: 'share',
|
||||
type: PageType.FOLLOWUP,
|
||||
followUpFlags: Constants.MessageFlags.IS_COMPONENTS_V2,
|
||||
render: async (pageCtx) => renderAppraisal(pageCtx.state.data.appraisal!, pageCtx, interaction),
|
||||
},
|
||||
},
|
||||
initialPage: 'appraiseModal',
|
||||
timeout: 300, // 5 minutes
|
||||
ephemeral: true,
|
||||
},
|
||||
interaction,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
definition,
|
||||
execute,
|
||||
};
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import type { ExecutableInteraction } from '@star-kitten/discord';
|
||||
import { createActionRow, createButton, createContainer, createTextDisplay } from '@star-kitten/discord/components';
|
||||
import type { PageContext } from '@star-kitten/discord/pages';
|
||||
import { type Appraisal } from '@star-kitten/eve/third-party/janice.js';
|
||||
import { formatNumberToShortForm } from '@star-kitten/util/text.js';
|
||||
import type { AppraisalState } from './appraise.command';
|
||||
|
||||
export function renderAppraisal(
|
||||
appraisal: Appraisal,
|
||||
pageCtx: PageContext<AppraisalState>,
|
||||
interaction: ExecutableInteraction,
|
||||
) {
|
||||
const formatter = new Intl.NumberFormat(interaction.locale || 'en-US', {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
|
||||
const container = createContainer(
|
||||
{
|
||||
accent_color: 0x1da57a,
|
||||
},
|
||||
createTextDisplay(`
|
||||
# [Appraisal ${appraisal.id} @ ${appraisal.market.name}](https://janice.e-351.com/a/${appraisal.id})
|
||||
### Buy: \`${formatter.format(appraisal.effectivePrices.totalBuyPrice)}\` ISK
|
||||
### Split: \`${formatter.format(appraisal.effectivePrices.totalSplitPrice)}\` ISK
|
||||
### Sell: \`${formatter.format(appraisal.effectivePrices.totalSellPrice)}\` ISK
|
||||
-# Volume: ${formatter.format(appraisal.totalPackagedVolume)} m³
|
||||
\`\`\`
|
||||
Buy: Sell: Qty: Item:
|
||||
${appraisal.items.map((i) => `${formatNumberToShortForm(i.effectivePrices.buyPrice).padEnd(10)}${formatNumberToShortForm(i.effectivePrices.sellPrice).padEnd(10)}${formatNumberToShortForm(i.amount).padEnd(10)}${i.itemType.name}`).join('\n')}
|
||||
\`\`\`
|
||||
-# https://janice.e-351.com/a/${appraisal.id}\n\n
|
||||
`),
|
||||
);
|
||||
|
||||
if (pageCtx.state.currentPage !== 'share') {
|
||||
container.components.push(
|
||||
createActionRow(
|
||||
createButton('Share in Channel', 'share', {
|
||||
disabled: !interaction.channel?.id,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 1,
|
||||
components: [container],
|
||||
};
|
||||
}
|
||||
79
packages/eve-bot/src/commands/appraise/renderAppraisal.tsx
Normal file
79
packages/eve-bot/src/commands/appraise/renderAppraisal.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import type { ExecutableInteraction } from '@star-kitten/discord';
|
||||
import * as StarKitten from '@star-kitten/discord';
|
||||
import { createActionRow, createButton, createContainer, createTextDisplay } from '@star-kitten/discord/components';
|
||||
import type { PageContext } from '@star-kitten/discord/pages';
|
||||
import { type Appraisal } from '@star-kitten/eve/third-party/janice.js';
|
||||
import { formatNumberToShortForm } from '@star-kitten/util/text.js';
|
||||
import type { AppraisalState } from './appraise.command';
|
||||
|
||||
export function renderAppraisal(
|
||||
appraisal: Appraisal,
|
||||
pageCtx: PageContext<AppraisalState>,
|
||||
interaction: ExecutableInteraction,
|
||||
): StarKitten.Component {
|
||||
const formatter = new Intl.NumberFormat(interaction.locale || 'en-US', {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
|
||||
// const container = createContainer(
|
||||
// {
|
||||
// accent_color: 0x1da57a,
|
||||
// },
|
||||
// createTextDisplay(`
|
||||
// # [Appraisal ${appraisal.id} @ ${appraisal.market.name}](https://janice.e-351.com/a/${appraisal.id})
|
||||
// ### Buy: \`${formatter.format(appraisal.effectivePrices.totalBuyPrice)}\` ISK
|
||||
// ### Split: \`${formatter.format(appraisal.effectivePrices.totalSplitPrice)}\` ISK
|
||||
// ### Sell: \`${formatter.format(appraisal.effectivePrices.totalSellPrice)}\` ISK
|
||||
// -# Volume: ${formatter.format(appraisal.totalPackagedVolume)} m³
|
||||
// \`\`\`
|
||||
// Buy: Sell: Qty: Item:
|
||||
// ${appraisal.items.map((i) => `${formatNumberToShortForm(i.effectivePrices.buyPrice).padEnd(10)}${formatNumberToShortForm(i.effectivePrices.sellPrice).padEnd(10)}${formatNumberToShortForm(i.amount).padEnd(10)}${i.itemType.name}`).join('\n')}
|
||||
// \`\`\`
|
||||
// -# https://janice.e-351.com/a/${appraisal.id}\n\n
|
||||
// `),
|
||||
// );
|
||||
|
||||
// if (pageCtx.state.currentPage !== 'share') {
|
||||
// container.components.push(
|
||||
// createActionRow(
|
||||
// createButton('Share in Channel', 'share', {
|
||||
// disabled: !interaction.channel?.id,
|
||||
// }),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// return {
|
||||
// type: 1,
|
||||
// components: [container],
|
||||
// };
|
||||
return (
|
||||
<container accent={0x1da57a}>
|
||||
<textDisplay
|
||||
content={`
|
||||
# [Appraisal ${appraisal.id} @ ${appraisal.market.name}](https://janice.e-351.com/a/${appraisal.id})
|
||||
### Buy: \`${formatter.format(appraisal.effectivePrices.totalBuyPrice)}\` ISK
|
||||
### Split: \`${formatter.format(appraisal.effectivePrices.totalSplitPrice)}\` ISK
|
||||
### Sell: \`${formatter.format(appraisal.effectivePrices.totalSellPrice)}\` ISK
|
||||
-# Volume: ${formatter.format(appraisal.totalPackagedVolume)} m³
|
||||
\`\`\`
|
||||
Buy: Sell: Qty: Item:
|
||||
${appraisal.items.map((i) => `${formatNumberToShortForm(i.effectivePrices.buyPrice).padEnd(10)}${formatNumberToShortForm(i.effectivePrices.sellPrice).padEnd(10)}${formatNumberToShortForm(i.amount).padEnd(10)}${i.itemType.name}`).join('\n')}
|
||||
\`\`\`
|
||||
-# https://janice.e-351.com/a/${appraisal.id}\n\n
|
||||
`}
|
||||
/>
|
||||
{pageCtx.state.currentPage !== 'share' ? (
|
||||
<actionRow>
|
||||
<button
|
||||
customId="share"
|
||||
label="Share in Channel"
|
||||
disabled={!interaction.channel?.id}
|
||||
style={StarKitten.ButtonStyle.PRIMARY}
|
||||
/>
|
||||
</actionRow>
|
||||
) : undefined}
|
||||
</container>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +1,37 @@
|
||||
import type { Interaction } from '@projectdysnomia/dysnomia';
|
||||
import { createModalLabel, createStringSelect, createTextInput } from '@star-kitten/discord/components';
|
||||
import { markets } from '@star-kitten/eve/third-party/janice.js';
|
||||
|
||||
export function renderAppraisalModal(interaction: Interaction) {
|
||||
return {
|
||||
// next page to render will be appraisalResult
|
||||
custom_id: `appraisalResult`,
|
||||
title: 'Appraise Items',
|
||||
components: [
|
||||
createModalLabel(
|
||||
'Select your market (default: Jita)',
|
||||
createStringSelect(
|
||||
'market',
|
||||
{
|
||||
placeholder: 'Select a market',
|
||||
},
|
||||
...markets.map((m) => ({
|
||||
label: m.name,
|
||||
value: m.id.toString(),
|
||||
default: m.id === 2, // Jita
|
||||
})),
|
||||
),
|
||||
),
|
||||
createModalLabel(
|
||||
'Enter items to appraise',
|
||||
createTextInput('input', {
|
||||
isParagraph: true,
|
||||
placeholder: `Enter list of items to be appraised.
|
||||
Tritanium 22222
|
||||
Pyerite 8000
|
||||
Mexallon 2444`,
|
||||
}),
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
import type { Interaction } from '@projectdysnomia/dysnomia';
|
||||
import { createModalLabel, createStringSelect, createTextInput } from '@star-kitten/discord/components';
|
||||
import { markets } from '@star-kitten/eve/third-party/janice.js';
|
||||
|
||||
export function renderAppraisalModal(interaction: Interaction) {
|
||||
return {
|
||||
// next page to render will be appraisalResult
|
||||
custom_id: `appraisalResult`,
|
||||
title: 'Appraise Items',
|
||||
components: [
|
||||
createModalLabel(
|
||||
'Select your market (default: Jita)',
|
||||
createStringSelect(
|
||||
'market',
|
||||
{
|
||||
placeholder: 'Select a market',
|
||||
},
|
||||
...markets.map((m) => ({
|
||||
label: m.name,
|
||||
value: m.id.toString(),
|
||||
default: m.id === 2, // Jita
|
||||
})),
|
||||
),
|
||||
),
|
||||
createModalLabel(
|
||||
'Enter items to appraise',
|
||||
createTextInput('input', {
|
||||
isParagraph: true,
|
||||
placeholder: `Enter list of items to be appraised.
|
||||
Tritanium 22222
|
||||
Pyerite 8000
|
||||
Mexallon 2444`,
|
||||
}),
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,140 +1,140 @@
|
||||
import { renderSubroutes, type Page } from '@star-kitten/discord/pages';
|
||||
import type { SearchState } from '../search.command';
|
||||
import {
|
||||
ButtonStyle,
|
||||
createContainer,
|
||||
createSection,
|
||||
createSeparator,
|
||||
createTextDisplay,
|
||||
createThumbnail,
|
||||
Padding,
|
||||
} from '@star-kitten/discord/components';
|
||||
import {
|
||||
getGroup,
|
||||
getType,
|
||||
getUnit,
|
||||
renderUnit,
|
||||
typeGetAttribute,
|
||||
typeHasAnyAttribute,
|
||||
type Type,
|
||||
} from '@star-kitten/eve/models';
|
||||
import { attributeOrdering } from '@star-kitten/eve';
|
||||
import { searchActionRow } from './helpers';
|
||||
import { toTitleCase } from '@star-kitten/util/text.js';
|
||||
|
||||
enum Images {
|
||||
ATTRIBUTES = 'https://iili.io/KTbaMR2.md.webp',
|
||||
DEFENSES = 'https://iili.io/KTbSVoX.md.webp',
|
||||
FITTING = 'https://iili.io/KufiFYG.md.webp',
|
||||
FACILITIES = 'https://iili.io/KufikGt.md.webp',
|
||||
}
|
||||
|
||||
const attributeCategoryMap = {
|
||||
structure: 'UI/Fitting/Structure',
|
||||
armor: 'UI/Common/Armor',
|
||||
shield: 'UI/Common/Shield',
|
||||
ewar: 'UI/Common/EWarResistances',
|
||||
capacitor: 'UI/Fitting/FittingWindow/Capacitor',
|
||||
targeting: 'UI/Fitting/FittingWindow/Targeting',
|
||||
facilities: 'UI/InfoWindow/SharedFacilities',
|
||||
fighters: 'UI/InfoWindow/FighterFacilities',
|
||||
on_death: 'UI/InfoWindow/OnDeath',
|
||||
jump_drive: 'UI/InfoWindow/JumpDriveSystems',
|
||||
propulsion: 'UI/Compare/Propulsion',
|
||||
};
|
||||
|
||||
const groupedCategories = [
|
||||
// defenses
|
||||
['shield', 'armor', 'structure', 'ewar'],
|
||||
// fittings
|
||||
['capacitor', 'targeting', 'propulsion'],
|
||||
// facilities
|
||||
['facilities', 'fighters', 'on_death', 'jump_drive'],
|
||||
];
|
||||
|
||||
function getAttributeOrdering(type: Type) {
|
||||
const group = getGroup(type.group_id);
|
||||
switch (group.category_id) {
|
||||
case 11:
|
||||
return attributeOrdering['11'];
|
||||
case 87:
|
||||
return attributeOrdering['87'];
|
||||
default:
|
||||
return attributeOrdering.default;
|
||||
}
|
||||
}
|
||||
|
||||
const bannerMap = {
|
||||
shield: Images.DEFENSES,
|
||||
armor: Images.DEFENSES,
|
||||
structure: Images.DEFENSES,
|
||||
ewar: Images.DEFENSES,
|
||||
|
||||
capacitor: Images.FITTING,
|
||||
targeting: Images.FITTING,
|
||||
propulsion: Images.FITTING,
|
||||
|
||||
facilities: Images.FACILITIES,
|
||||
fighters: Images.FACILITIES,
|
||||
on_death: Images.FACILITIES,
|
||||
jump_drive: Images.FACILITIES,
|
||||
};
|
||||
|
||||
const page: Page<SearchState> = {
|
||||
key: 'attributes',
|
||||
render: (context) => {
|
||||
const type = getType(context.state.data.type_id);
|
||||
const ordering = getAttributeOrdering(type);
|
||||
|
||||
return {
|
||||
components: [
|
||||
createContainer(
|
||||
{},
|
||||
createSection(
|
||||
createThumbnail(`https://images.evetech.net/types/${type.type_id}/icon`),
|
||||
createTextDisplay(`# [${type.name.en}](https://everef.net/types/${type.type_id})\n## Attributes`),
|
||||
),
|
||||
...renderSubroutes(
|
||||
context,
|
||||
'attributes',
|
||||
groupedCategories.map((group) =>
|
||||
group.map((cat) => {
|
||||
const attrCat = ordering[attributeCategoryMap[cat]];
|
||||
const attrs = attrCat.groupedCategories
|
||||
? attrCat.groupedCategories.map(([name, id]) => id).concat(attrCat.normalAttributes) || []
|
||||
: attrCat.normalAttributes;
|
||||
if (!typeHasAnyAttribute(type, attrs)) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
label: toTitleCase(cat.replace('_', ' ')),
|
||||
value: cat,
|
||||
banner: bannerMap[cat],
|
||||
};
|
||||
}),
|
||||
),
|
||||
(currentRoute) => {
|
||||
const lines: string[] = [];
|
||||
const attrCat = ordering[attributeCategoryMap[currentRoute]];
|
||||
const attrs = attrCat.groupedCategories
|
||||
? attrCat.groupedCategories.map(([name, id]) => id).concat(attrCat.normalAttributes) || []
|
||||
: attrCat.normalAttributes;
|
||||
attrs.map((attrId) => {
|
||||
const attr = typeGetAttribute(type, attrId);
|
||||
if (!attr) return;
|
||||
const unit = attr.attribute.unit_id ? renderUnit(getUnit(attr.attribute.unit_id), attr.value) : '';
|
||||
lines.push(`${attr.attribute.display_name.en.padEnd(24)} ${unit}`);
|
||||
});
|
||||
return createTextDisplay('```\n' + lines.join('\n') + '\n```');
|
||||
},
|
||||
{ style: ButtonStyle.SECONDARY },
|
||||
),
|
||||
createSeparator(Padding.LARGE),
|
||||
searchActionRow('attributes'),
|
||||
),
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default page;
|
||||
import { renderSubroutes, type Page } from '@star-kitten/discord/pages';
|
||||
import type { SearchState } from '../search.command';
|
||||
import {
|
||||
ButtonStyle,
|
||||
createContainer,
|
||||
createSection,
|
||||
createSeparator,
|
||||
createTextDisplay,
|
||||
createThumbnail,
|
||||
Padding,
|
||||
} from '@star-kitten/discord/components';
|
||||
import {
|
||||
getGroup,
|
||||
getType,
|
||||
getUnit,
|
||||
renderUnit,
|
||||
typeGetAttribute,
|
||||
typeHasAnyAttribute,
|
||||
type Type,
|
||||
} from '@star-kitten/eve/models';
|
||||
import { attributeOrdering } from '@star-kitten/eve';
|
||||
import { searchActionRow } from './helpers';
|
||||
import { toTitleCase } from '@star-kitten/util/text.js';
|
||||
|
||||
enum Images {
|
||||
ATTRIBUTES = 'https://iili.io/KTbaMR2.md.webp',
|
||||
DEFENSES = 'https://iili.io/KTbSVoX.md.webp',
|
||||
FITTING = 'https://iili.io/KufiFYG.md.webp',
|
||||
FACILITIES = 'https://iili.io/KufikGt.md.webp',
|
||||
}
|
||||
|
||||
const attributeCategoryMap = {
|
||||
structure: 'UI/Fitting/Structure',
|
||||
armor: 'UI/Common/Armor',
|
||||
shield: 'UI/Common/Shield',
|
||||
ewar: 'UI/Common/EWarResistances',
|
||||
capacitor: 'UI/Fitting/FittingWindow/Capacitor',
|
||||
targeting: 'UI/Fitting/FittingWindow/Targeting',
|
||||
facilities: 'UI/InfoWindow/SharedFacilities',
|
||||
fighters: 'UI/InfoWindow/FighterFacilities',
|
||||
on_death: 'UI/InfoWindow/OnDeath',
|
||||
jump_drive: 'UI/InfoWindow/JumpDriveSystems',
|
||||
propulsion: 'UI/Compare/Propulsion',
|
||||
};
|
||||
|
||||
const groupedCategories = [
|
||||
// defenses
|
||||
['shield', 'armor', 'structure', 'ewar'],
|
||||
// fittings
|
||||
['capacitor', 'targeting', 'propulsion'],
|
||||
// facilities
|
||||
['facilities', 'fighters', 'on_death', 'jump_drive'],
|
||||
];
|
||||
|
||||
function getAttributeOrdering(type: Type) {
|
||||
const group = getGroup(type.group_id);
|
||||
switch (group.category_id) {
|
||||
case 11:
|
||||
return attributeOrdering['11'];
|
||||
case 87:
|
||||
return attributeOrdering['87'];
|
||||
default:
|
||||
return attributeOrdering.default;
|
||||
}
|
||||
}
|
||||
|
||||
const bannerMap = {
|
||||
shield: Images.DEFENSES,
|
||||
armor: Images.DEFENSES,
|
||||
structure: Images.DEFENSES,
|
||||
ewar: Images.DEFENSES,
|
||||
|
||||
capacitor: Images.FITTING,
|
||||
targeting: Images.FITTING,
|
||||
propulsion: Images.FITTING,
|
||||
|
||||
facilities: Images.FACILITIES,
|
||||
fighters: Images.FACILITIES,
|
||||
on_death: Images.FACILITIES,
|
||||
jump_drive: Images.FACILITIES,
|
||||
};
|
||||
|
||||
const page: Page<SearchState> = {
|
||||
key: 'attributes',
|
||||
render: (context) => {
|
||||
const type = getType(context.state.data.type_id);
|
||||
const ordering = getAttributeOrdering(type);
|
||||
|
||||
return {
|
||||
components: [
|
||||
createContainer(
|
||||
{},
|
||||
createSection(
|
||||
createThumbnail(`https://images.evetech.net/types/${type.type_id}/icon`),
|
||||
createTextDisplay(`# [${type.name.en}](https://everef.net/types/${type.type_id})\n## Attributes`),
|
||||
),
|
||||
...renderSubroutes(
|
||||
context,
|
||||
'attributes',
|
||||
groupedCategories.map((group) =>
|
||||
group.map((cat) => {
|
||||
const attrCat = ordering[attributeCategoryMap[cat]];
|
||||
const attrs = attrCat.groupedCategories
|
||||
? attrCat.groupedCategories.map(([name, id]) => id).concat(attrCat.normalAttributes) || []
|
||||
: attrCat.normalAttributes;
|
||||
if (!typeHasAnyAttribute(type, attrs)) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
label: toTitleCase(cat.replace('_', ' ')),
|
||||
value: cat,
|
||||
banner: bannerMap[cat],
|
||||
};
|
||||
}),
|
||||
),
|
||||
(currentRoute) => {
|
||||
const lines: string[] = [];
|
||||
const attrCat = ordering[attributeCategoryMap[currentRoute]];
|
||||
const attrs = attrCat.groupedCategories
|
||||
? attrCat.groupedCategories.map(([name, id]) => id).concat(attrCat.normalAttributes) || []
|
||||
: attrCat.normalAttributes;
|
||||
attrs.map((attrId) => {
|
||||
const attr = typeGetAttribute(type, attrId);
|
||||
if (!attr) return;
|
||||
const unit = attr.attribute.unit_id ? renderUnit(getUnit(attr.attribute.unit_id), attr.value) : '';
|
||||
lines.push(`${attr.attribute.display_name.en.padEnd(24)} ${unit}`);
|
||||
});
|
||||
return createTextDisplay('```\n' + lines.join('\n') + '\n```');
|
||||
},
|
||||
{ style: ButtonStyle.SECONDARY },
|
||||
),
|
||||
createSeparator(Padding.LARGE),
|
||||
searchActionRow('attributes'),
|
||||
),
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default page;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createActionRow, createButton } from '@star-kitten/discord/components';
|
||||
|
||||
export function searchActionRow(pageKey: string) {
|
||||
return createActionRow(
|
||||
createButton('Main', 'main', { disabled: pageKey === 'main' }),
|
||||
createButton('Attributes', 'attributes', { disabled: pageKey === 'attributes' }),
|
||||
createButton('Fittings', 'fittings', { disabled: pageKey === 'fittings' }),
|
||||
createButton('Skills', 'skills', { disabled: pageKey === 'skills' }),
|
||||
createButton('Industry', 'industry', { disabled: pageKey === 'industry' }),
|
||||
);
|
||||
}
|
||||
import { createActionRow, createButton } from '@star-kitten/discord/components';
|
||||
|
||||
export function searchActionRow(pageKey: string) {
|
||||
return createActionRow(
|
||||
createButton('Main', 'main', { disabled: pageKey === 'main' }),
|
||||
createButton('Attributes', 'attributes', { disabled: pageKey === 'attributes' }),
|
||||
createButton('Fittings', 'fittings', { disabled: pageKey === 'fittings' }),
|
||||
createButton('Skills', 'skills', { disabled: pageKey === 'skills' }),
|
||||
createButton('Industry', 'industry', { disabled: pageKey === 'industry' }),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
import type { Page } from '@star-kitten/discord/pages';
|
||||
import type { SearchState } from '../search.command';
|
||||
import {
|
||||
createContainer,
|
||||
createMediaGallery,
|
||||
createSection,
|
||||
createTextDisplay,
|
||||
createThumbnail,
|
||||
createURLButton,
|
||||
} from '@star-kitten/discord/components';
|
||||
import { getRoleBonuses, getSkillBonuses, getType } from '@star-kitten/eve/models/type.js';
|
||||
import { cleanText } from '@star-kitten/eve/utils/markdown.js';
|
||||
import { typeSearch } from '@star-kitten/eve/utils/typeSearch.js';
|
||||
import { isApplicationCommand } from '@star-kitten/discord';
|
||||
import { fetchPrice } from '@star-kitten/eve/third-party/evetycoon.js';
|
||||
import { formatNumberToShortForm } from '@star-kitten/util/text.js';
|
||||
import { searchActionRow } from './helpers';
|
||||
|
||||
const page: Page<SearchState> = {
|
||||
key: 'main',
|
||||
render: async (context) => {
|
||||
if (!context.state.data.type_id && isApplicationCommand(context.interaction)) {
|
||||
const typeName = context.interaction.data.options?.find((opt) => opt.name === 'name')?.value;
|
||||
const found = await typeSearch(typeName as string);
|
||||
|
||||
if (!found) {
|
||||
return {
|
||||
components: [createTextDisplay(`No item found for: ${typeName}`)],
|
||||
};
|
||||
}
|
||||
|
||||
context.state.data.type_id = found.type_id;
|
||||
}
|
||||
|
||||
const type = getType(context.state.data.type_id);
|
||||
|
||||
const skillBonuses = getSkillBonuses(type);
|
||||
const roleBonuses = getRoleBonuses(type);
|
||||
const price = await fetchPrice(type.type_id);
|
||||
|
||||
return {
|
||||
components: [
|
||||
createContainer(
|
||||
{},
|
||||
createSection(
|
||||
createThumbnail(`https://images.evetech.net/types/${type.type_id}/icon`),
|
||||
createTextDisplay(`
|
||||
# [${type.name.en}](https://everef.net/types/${type.type_id})
|
||||
|
||||
${skillBonuses
|
||||
.map((bonus) => {
|
||||
return `## Bonus per level of ${bonus.skill.name.en}
|
||||
${bonus.bonuses
|
||||
.sort((a, b) => a.importance - b.importance)
|
||||
.map((b) => `${b.bonus}${b.unit?.display_name ?? '-'} ${cleanText(b.bonus_text.en)}`)
|
||||
.join('\n')}`;
|
||||
})
|
||||
.join('\n')}
|
||||
${
|
||||
roleBonuses.length > 0
|
||||
? `\n## Role Bonuses
|
||||
${roleBonuses
|
||||
.sort((a, b) => a.importance - b.importance)
|
||||
.map((b) => `${b.bonus ?? ''}${b.unit?.display_name ?? '-'} ${cleanText(b.bonus_text.en)}`)
|
||||
.join('\n')}`
|
||||
: ''
|
||||
}
|
||||
`),
|
||||
),
|
||||
createMediaGallery({
|
||||
url: 'https://iili.io/KTPCFRt.md.webp',
|
||||
}),
|
||||
// createSeparator(Padding.LARGE),
|
||||
createSection(
|
||||
createURLButton('View on EVE Tycoon', `https://evetycoon.com/market/${type.type_id}`),
|
||||
createTextDisplay(
|
||||
`## Buy: ${price ? formatNumberToShortForm(price.buyAvgFivePercent) : '--'} ISK
|
||||
## Sell: ${price ? formatNumberToShortForm(price.sellAvgFivePercent) : '--'} ISK`,
|
||||
),
|
||||
),
|
||||
createTextDisplay(`-# Type Id: ${type.type_id}`),
|
||||
searchActionRow('main'),
|
||||
),
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default page;
|
||||
import type { Page } from '@star-kitten/discord/pages';
|
||||
import type { SearchState } from '../search.command';
|
||||
import {
|
||||
createContainer,
|
||||
createMediaGallery,
|
||||
createSection,
|
||||
createTextDisplay,
|
||||
createThumbnail,
|
||||
createURLButton,
|
||||
} from '@star-kitten/discord/components';
|
||||
import { getRoleBonuses, getSkillBonuses, getType } from '@star-kitten/eve/models/type.js';
|
||||
import { cleanText } from '@star-kitten/eve/utils/markdown.js';
|
||||
import { typeSearch } from '@star-kitten/eve/utils/typeSearch.js';
|
||||
import { isApplicationCommand } from '@star-kitten/discord';
|
||||
import { fetchPrice } from '@star-kitten/eve/third-party/evetycoon.js';
|
||||
import { formatNumberToShortForm } from '@star-kitten/util/text.js';
|
||||
import { searchActionRow } from './helpers';
|
||||
|
||||
const page: Page<SearchState> = {
|
||||
key: 'main',
|
||||
render: async (context) => {
|
||||
if (!context.state.data.type_id && isApplicationCommand(context.interaction)) {
|
||||
const typeName = context.interaction.data.options?.find((opt) => opt.name === 'name')?.value;
|
||||
const found = await typeSearch(typeName as string);
|
||||
|
||||
if (!found) {
|
||||
return {
|
||||
components: [createTextDisplay(`No item found for: ${typeName}`)],
|
||||
};
|
||||
}
|
||||
|
||||
context.state.data.type_id = found.type_id;
|
||||
}
|
||||
|
||||
const type = getType(context.state.data.type_id);
|
||||
|
||||
const skillBonuses = getSkillBonuses(type);
|
||||
const roleBonuses = getRoleBonuses(type);
|
||||
const price = await fetchPrice(type.type_id);
|
||||
|
||||
return {
|
||||
components: [
|
||||
createContainer(
|
||||
{},
|
||||
createSection(
|
||||
createThumbnail(`https://images.evetech.net/types/${type.type_id}/icon`),
|
||||
createTextDisplay(`
|
||||
# [${type.name.en}](https://everef.net/types/${type.type_id})
|
||||
|
||||
${skillBonuses
|
||||
.map((bonus) => {
|
||||
return `## Bonus per level of ${bonus.skill.name.en}
|
||||
${bonus.bonuses
|
||||
.sort((a, b) => a.importance - b.importance)
|
||||
.map((b) => `${b.bonus}${b.unit?.display_name ?? '-'} ${cleanText(b.bonus_text.en)}`)
|
||||
.join('\n')}`;
|
||||
})
|
||||
.join('\n')}
|
||||
${
|
||||
roleBonuses.length > 0
|
||||
? `\n## Role Bonuses
|
||||
${roleBonuses
|
||||
.sort((a, b) => a.importance - b.importance)
|
||||
.map((b) => `${b.bonus ?? ''}${b.unit?.display_name ?? '-'} ${cleanText(b.bonus_text.en)}`)
|
||||
.join('\n')}`
|
||||
: ''
|
||||
}
|
||||
`),
|
||||
),
|
||||
createMediaGallery({
|
||||
url: 'https://iili.io/KTPCFRt.md.webp',
|
||||
}),
|
||||
// createSeparator(Padding.LARGE),
|
||||
createSection(
|
||||
createURLButton('View on EVE Tycoon', `https://evetycoon.com/market/${type.type_id}`),
|
||||
createTextDisplay(
|
||||
`## Buy: ${price ? formatNumberToShortForm(price.buyAvgFivePercent) : '--'} ISK
|
||||
## Sell: ${price ? formatNumberToShortForm(price.sellAvgFivePercent) : '--'} ISK`,
|
||||
),
|
||||
),
|
||||
createTextDisplay(`-# Type Id: ${type.type_id}`),
|
||||
searchActionRow('main'),
|
||||
),
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default page;
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
import {
|
||||
createChatCommand,
|
||||
isAutocomplete,
|
||||
stringOption,
|
||||
type CommandContext,
|
||||
type ExecutableInteraction,
|
||||
} from '@star-kitten/discord';
|
||||
import { usePages } from '@star-kitten/discord/pages';
|
||||
import { initializeTypeSearch, typeSearchAutoComplete } from '@star-kitten/eve/utils/typeSearch.js';
|
||||
|
||||
import main from './pages/main';
|
||||
import attributes from './pages/attributes';
|
||||
|
||||
let now = Date.now();
|
||||
console.debug('Initializing type search...');
|
||||
await initializeTypeSearch().catch((e) => {
|
||||
console.error('Failed to initialize type search', e);
|
||||
process.exit(1);
|
||||
});
|
||||
console.debug(`Type search initialized. Took ${Date.now() - now}ms`);
|
||||
|
||||
export interface SearchState {
|
||||
type_id: number;
|
||||
}
|
||||
|
||||
export default createChatCommand(
|
||||
{
|
||||
name: 'search',
|
||||
description: 'Search for a type',
|
||||
options: [
|
||||
stringOption({
|
||||
name: 'name',
|
||||
description: 'The type name to search for',
|
||||
autocomplete: true,
|
||||
required: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
execute,
|
||||
);
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
if (isAutocomplete(interaction)) {
|
||||
const focusedOption = interaction.data.options?.find((opt) => opt.focused);
|
||||
if (focusedOption?.name === 'name') {
|
||||
const value = focusedOption.value as string;
|
||||
const results = await typeSearchAutoComplete(value);
|
||||
if (results) {
|
||||
await interaction.result(results);
|
||||
} else {
|
||||
await interaction.result([]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
usePages<SearchState>(
|
||||
{
|
||||
pages: {
|
||||
main,
|
||||
attributes,
|
||||
},
|
||||
initialPage: 'main',
|
||||
ephemeral: false,
|
||||
},
|
||||
interaction,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
import {
|
||||
createChatCommand,
|
||||
isAutocomplete,
|
||||
stringOption,
|
||||
type CommandContext,
|
||||
type ExecutableInteraction,
|
||||
} from '@star-kitten/discord';
|
||||
import { usePages } from '@star-kitten/discord/pages';
|
||||
import { initializeTypeSearch, typeSearchAutoComplete } from '@star-kitten/eve/utils/typeSearch.js';
|
||||
|
||||
import main from './pages/main';
|
||||
import attributes from './pages/attributes';
|
||||
|
||||
let now = Date.now();
|
||||
console.debug('Initializing type search...');
|
||||
await initializeTypeSearch().catch((e) => {
|
||||
console.error('Failed to initialize type search', e);
|
||||
process.exit(1);
|
||||
});
|
||||
console.debug(`Type search initialized. Took ${Date.now() - now}ms`);
|
||||
|
||||
export interface SearchState {
|
||||
type_id: number;
|
||||
}
|
||||
|
||||
export default createChatCommand(
|
||||
{
|
||||
name: 'search',
|
||||
description: 'Search for a type',
|
||||
options: [
|
||||
stringOption({
|
||||
name: 'name',
|
||||
description: 'The type name to search for',
|
||||
autocomplete: true,
|
||||
required: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
execute,
|
||||
);
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
if (isAutocomplete(interaction)) {
|
||||
const focusedOption = interaction.data.options?.find((opt) => opt.focused);
|
||||
if (focusedOption?.name === 'name') {
|
||||
const value = focusedOption.value as string;
|
||||
const results = await typeSearchAutoComplete(value);
|
||||
if (results) {
|
||||
await interaction.result(results);
|
||||
} else {
|
||||
await interaction.result([]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
usePages<SearchState>(
|
||||
{
|
||||
pages: {
|
||||
main,
|
||||
attributes,
|
||||
},
|
||||
initialPage: 'main',
|
||||
ephemeral: false,
|
||||
},
|
||||
interaction,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { startDiscordBot } from '@star-kitten/discord';
|
||||
|
||||
startDiscordBot();
|
||||
import { startDiscordBot } from '@star-kitten/discord';
|
||||
|
||||
startDiscordBot();
|
||||
|
||||
26
packages/eve-bot/src/test.tsx
Normal file
26
packages/eve-bot/src/test.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
export function renderAppraisal() {
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
const world = 'world';
|
||||
const rand = Math.random() * 1000;
|
||||
const pageCtx = { state: { currentPage: 'home' } };
|
||||
|
||||
let jsx = (
|
||||
<actionRow>
|
||||
<container accent={0x1da57a}>
|
||||
<textDisplay content={`Hello ${world}`} />
|
||||
{pageCtx.state.currentPage !== 'share' ? (
|
||||
<actionRow>
|
||||
<button customId="share" label="Share in Channel" disabled={rand < 500} />
|
||||
</actionRow>
|
||||
) : undefined}
|
||||
</container>
|
||||
</actionRow>
|
||||
);
|
||||
|
||||
console.log(jsx);
|
||||
}
|
||||
|
||||
renderAppraisal();
|
||||
@@ -1,38 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"composite": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@star-kitten/discord",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": false,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
|
||||
// Paths
|
||||
"paths": {
|
||||
"@*": ["./src/*"]
|
||||
},
|
||||
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
||||
"typeRoots": ["src/types", "./node_modules/@types"]
|
||||
},
|
||||
"include": ["src", "types"],
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY_DEVELOPMENT="02572da3d4f3a844588a944214c0e142a5a01deaa6551456af146d34b574024416"
|
||||
|
||||
# .env.development
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY="02292a330aa041b5f7efc51504e0c208accba67a6877a217ab43cbb59c3c0c3e66"
|
||||
|
||||
# .env
|
||||
DEBUG="encrypted:BC7p62nrs3NV7XdxnBbO1WsHGm8IgDEbOS1RmgORHSqh05vGIv+hmqwau61FamrU/puT4btAsG+iLcSeypCQV5e7bBpr0qu0HQoVyMzunBvrN5ivzNY0Af800lNynsBXzq0cXTY="
|
||||
PORT="encrypted:BJpY7J2J+0z4LUNnKRr7HzcpETcdnWFuRAOC3hVl2cZyiCBl706vJqv+iY3BgA0mus73t9fwYjGRrPSXSQbcSEBzr+Jquj8Gkvy7loXKkKp4Gz1tqX554txfY0XjrgMO3oHATO8="
|
||||
NODE_ENV="encrypted:BJun6Kdf/kBSrIYUgw7pfnMwlrTjvUUq/w2yjqn+X5UgaxUxzLsI0JabYlxQCxoDMSEagQYWI5HkRaZvYuXHzyN2aXm6drC2bahg9aWZTVyWYu00FFwIah7l/tuMA/caeO7s5dwkuOgCvlOQ"
|
||||
LOG_LEVEL="encrypted:BKBvZDS7xkzgg5IqiTc9izmt4om4CjX7t4LA8gMY+0ru0NtVBpkSkchil4PvaRNhktcNjtIzfE6sduRoFw5T1tt88PjtKWIhORyACZk2ZtR3vuO/xecq2q1rIIp5kD1gcp4ujltK"
|
||||
BASE_URL="encrypted:BMueek9QzKR3k+Foe0xUruZUxwwIXBcrL7B6ksdBNFaF92nHHy2HLLOhOpKNebGfSwb9mBR5kOXb095+hsDEcqAiv5nc5BjWGfk/wkFpRfFcmyEGYlmrxLclbFdQFXIqYrzmN7ae/VV1VlRzNLBipO0smJj4LZY649oV2A=="
|
||||
EVE_CLIENT_ID="encrypted:BPOAohc2MmP7VvvLybwvcl7XgzYRqWC6IzBkOf+T73kN8YlHYKt611uYNjU27G6hkVRK3DSfgCuxTPzrQBPxvAY6pDbqFG8pVU9cKDmoeHYtiKh7KkHuBN/cEzll6x8hpIwwrY32nzQjxYMvyVO5UgG7OHK1T/jkeya2TW2DSgGn"
|
||||
EVE_CLIENT_SECRET="encrypted:BE0VYniO3JEFNt1R4iUrGA7W7cSp7gQtG7Y86VQeWnte+idjnqSFmv2lmz83rc7Idvi/VU+ipuY6RL2+49jAb/oUaXGUiwguBAnlFU+ypOVy2Ed29o7yggqiB2+dUuu4xDAsLAfSXErnw4gsDsEPAMqaKhCYz0LHEvJX5ZdwfAcfrpWoLeI/Vm0="
|
||||
EVE_CALLBACK_URL="encrypted:BIjACAnGtL06X0vkvmydydup9HZDcPA+DAYUAhlH3lsq8GJPD5XxlRwVx02VzZQuATfqm1JwGDyYbw8ceaWD2RLlcjSXPF7MWDpYzG1FExE7FZbFRBBO8XqGH9X8kfxYsuca/Td8KuIPjyS5BNkyM4GQcTlojKa15Hk4GXmpNP6Gyb45XNPCegRJL5aARQ=="
|
||||
ESI_USER_AGENT="encrypted:BEUaqMbwPNvJeF/d5q5LJ5Owd5wcQ8Jg/BTGn6qns4cwlX/e6QqLLmfp8E8SxQ8Z6h+qDLpZj0HROJIOK9Y2Xb6qjB+hCnjceRMTx1QWpNS6jXQ85TQiZfYzee57QFleau621B77KIuM5DjPUZZ02efAL+2Yk83amrh3vnzvwrnvM5mMGTzc2TbeQwB6MEdjAdvAz65VGX9DnCwbtP3bMKE7og4+sKMUrTYZpCsILug="
|
||||
DISCORD_APP_ID="encrypted:BM3yHCf9kTxcIQzzmNseT0/xol6ZLYTjZ3m3NKybW1oD2joZ/gTUIg2+mgfaeCqY1CPaSGppxguDPFgthMbWihAdeGuxiITiwLDulCTLcgjBsyT6IlsKUSsE5ZiEZl1A+ikNG1/8rxrF0MsIjVqfw+U+ev8="
|
||||
DISCORD_APP_SECRET="encrypted:BNVLmKb2ZJq1+iIYKwGaCtcM+hak5NPXLNgJnPzTlpd/5zUTKc8OtYZLhg2oqtOYv8rxf8sbjpXNWB6lZL5J5NUuPcQSfVOMr3U1BN6b9WsfWZ/2Pr4cm8kqqqjJVTF49/DasRQU5VlIXodvCI2XfWrcPFV20NvE8HzsJQz/g5cF"
|
||||
DISCORD_PUBLIC_KEY="encrypted:BEmxCcAqODfgukOza3EGzsXdrnXv3qVbzlLFYsD4Iba9mQFCaOwChIQNkN/+Ve5NV5sZeefU5sEXZRYbb/hRjJjMRX4NrhEZn2Pg3mI5/FG/+uCD0cWWs4JGzTRwUcBcG8FZ2Mw/kP1ymUqMRkJCYC+XdyYqcP8zNrQ6/aca3HLcqPma7j2/1lsbX4UJ3QcKDg3bsY+107MLLJJ4+TYiwcE="
|
||||
DISCORD_BOT_TOKEN="encrypted:BCLvoICUVOYz7pkV3f+dufxEZiWcKxQ7E56cCUXtLQjjorW5WsCftgoCdP5NXKSbhHYZBqrTCAHqPp/kLgaVjDXxXu2+61DiCNjB3t/kNIudkfsuzJ0vusKcVzgnQOmTmYnOZ+SYo7hKPrjLi2/Vk8r6K26TATp9t+iuSBMsEs7t9nnnySSYIXyHfXRXbRBg4NUFOmeDRUqBhhix9mgr+MI5SPmQgsXCvA=="
|
||||
DISCORD_TEST_GUILD_ID="encrypted:BKRJRRvQw2aoBrVoVZpPPYSzCy+2VXLLzBb1zNzUR5510qobPNDbUoIlJdm41moQS0ALG94l8miVLtkOKW6MGZyepq+gE1zSEu7eIJWHB8/eSUnGuNeqSghI4Kxr8kn04Bl4eDb1UgP1Fu+8XSXjVel85Q=="
|
||||
JANICE_KEY="encrypted:BPq+CJycBbmmoDHNmHHYiQ00PeISDQTqp2IdlLZD52V2wWPOmXnRnqgvoXff64ebUayySaW1sQtvoaMJE3Gt9E/FUquYiRTPUMT5+oW7Xo60IRgAhRW9n2m/YUDIORxR0J0qxKc9cdE75VWJNqLELKKwsriBdpqj0+4yM/Mn6IaU"
|
||||
PERPLEXITY_API_KEY="encrypted:BHxscbb6WwhFgt5Cp/WzrYGJ6nJvIFeoS3JrTaibPbQ7kiu3C/Zx8klgRhlBF25+HAA8chZHT5MeE99FSD59RqIQTcnKAyfUnxNq+ovoXMV7Nk2Fb/rrdLnvUf8VoeH7L4ZyOVF1wv0r4xm/7Qqc1JN50fME6Qa47KnnQekq/n6o2e5HVI153yJnlCQu/SvcyXCY6VW2"
|
||||
@@ -1,23 +0,0 @@
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY_PRODUCTION="02f0469506f6722d8fcc179c199ff159ca32f082000c8e7a1465891adb50a4c031"
|
||||
|
||||
# .env.production
|
||||
DEBUG="encrypted:BKnKyDjoQANl7bGi3568JTnA/7sUBdUVlc8nNznTwxs5Lu/4iMDu1cY0x0iE7b0z+RXEuA/w6bERB5uTmWkCdglEI5S6LhnXZzcV55iLBY8rHO/MIDBF39vn/PBsMiA86gBmtaIn"
|
||||
PORT="encrypted:BKaGvooBBBWB13491yjfYwYT2zG1MZiYi3+Y6wW8ZhuvMOEsfPuW94rv3cE2LoguALsBFXH9rn3lQJysZLJcYd9AxJoLqxmWHkEpbQ35PYDSvkJ0GsEGlrd74hVbnh57A7Dmqqc="
|
||||
NODE_ENV="encrypted:BIiY7XZ9vy9stbiNC2u+o4ibruGTtMuRXUJPs3lMxkHKlK6gksg0ddTPbia/qZkZudGjnEhmqYDUPfSWQDdmf8gFsuBgSgYGhR2GCNw7mCCmsDO/wE3ojNnlvuetnVfLeJa7ugvOGjX5QTs="
|
||||
LOG_LEVEL="encrypted:BEAJviH6nTAR4AdFEoiud4ZHV+dwvURoZys4M9KsYAn5MD+nlNEnzS+9vtE3NPwqzfkpK0Z/46xB+SUIXwhwaJ9Yzgz2WqLK1UXEB6fhQqHXdIqvW2ug/+hUxW9k0ueMu5I9btE="
|
||||
BASE_URL="encrypted:BJ6YYd2cX31HuTvGnNxLK33KQgzWxU9yRtlwAc79hhbuioHP5lMu7LxCu8NnXfcaEvevWsEt3cG4I7PNqzlBmU1WhTwsdJ4Kqi2cvs6hKwMeVWvtgdI+ymojG/GoglkbHjSc9737dsth2+erI4qbjkafqYuOC9S9"
|
||||
DISCORD_APP_ID="encrypted:BMqY+6wep/q7bWO9Yc/tJukPHH5H6vvItZpOFK5zaLP92Fx4S+qyZvH/LZKfUBSxZ1d1vbAqo4V+HPxNPyvwXaZyu/qlRf5ZTP/hkt1k9RoNb5UNOMSoD6GSZFS24/JEKuDPzQnvEOb0+prPHEJknvTMXmI="
|
||||
DISCORD_APP_SECRET="encrypted:BBBWkXA3zi8rCWEiMH66v1hFb6Jqw0Bn4H/6b5qWbVakm5UDckOmgQSjXosJLA1VX0vY+38s5fT1ICPIBv2b0rzpaQ09GpRIwryyTT+VLvMcZfrt1CP4ISC0uzlE2p9qceK9EG+6I7ge4pRkzpxotwnWAg9SCqVcNb04kRwPhUvj"
|
||||
DISCORD_PUBLIC_KEY="encrypted:BPqsvWPJ7/IBYynqFMHmEcpMBS+T5CXyfnzZU3flouPwcCaKkIc3xkLhtVco7nWdv2v/hw9ulysmVJhT6CiW9r1k0XRqRnv4q1PDDSTLrP0c1cXPg6pFLcOEv7e2CU+Gkj7UEFZz3xgrb6aWkkLRO0yATcA20xbdnOv3rbigOrJCfJwbbAWPQk7yun8oF6O/mGvWW9Blv8if54fp8vBOeUs="
|
||||
DISCORD_BOT_TOKEN="encrypted:BMfVvYAKvIsMW+dS1X5SZcXfUIxrsunb3q4iXM2ifTlSP8ZlFpA2jxvgYj16jKcexiyVUBhWMSyUC4eS2AuIY/6fKCuA/5JtvqMltzcxPYAi3VEYIz3AgURiMRNy2QsNnqEXxNiakNaPq5Tv4dqVB4z7YeQ16QdvpDWxD4XUQFtG8Q5jZQK/ISj4xJT9cmaxLUJB9XQcbAa8Oseghpk/A1i5JXO05Rx4vQ=="
|
||||
EVE_CLIENT_ID="encrypted:BCBtASu/2DN+EvLPM//WQjBdfwRscwSC5zKBHLTTTcXMfVur8GtDB6ZcBWstmC4YdiimjPobi1RJ+qdYndu1SM300g6UwOmmO2sNhOpG5nyP0mT2HNgwcJzl+Z7Ad1Vr/iByzYyqkc+uYr9NwhvJDPud+HP11dTjKvw+9Ht9/abA"
|
||||
EVE_CLIENT_SECRET="encrypted:BMccZ43R9rT33amzo0zfIgLM8hKDCMXLrj+5h0TNLH1RhwrsUxcKgl17MAVqV+8uPBbB171kRRnjKLaQDjjJM27Jv1SV5bn316qrIx35Tkl2Ocd5wjEs7TSAjf8HwzUhiH9F68+IrQ36Vm8w27+RmsaRtvTtiWWVmYvBXw4PFMprTE7SG0bFq3M="
|
||||
EVE_CALLBACK_URL="encrypted:BDsEZqRGXFzRigkBq50UYj14UvNjRM4Ao3PLSxVTeyc+2Fad1DQa9mfFE9yBnp3l1H5KMQcPJdWf/MxyAa9J0RvXm9l01lbmkgXu+C+HJXWHKJ7/b91NrQqngm2l76jp80WjtmdJ2D5WOUGrIxZatQaqgh8TexQAjVwjkTeQO97PJnbF0FyNQOlu"
|
||||
ESI_USER_AGENT="encrypted:BJ9Pib5a8/qxfROzBfjlAKr/fEvgepN8o6NCI0l3aiYvFuk5hczaA57TKPMP6P2Ct/Juj47YuU8bqF147y8C556NMiY2HDPbrnenXKdAh4xCerjXhkFqowPvEMVxoeuiyhRM7mPmUSAw7AbYi7AxDtCTw80/6S2/b9/32XBk4eCnSdJmM9kFxwHVFQNK83V0Sr5XEymT4S1kntvqlFsBel/5KxMMfNieqTiT+b5mVyM="
|
||||
AUTH_DB_PATH="encrypted:BAqssA/4tJHhxv+pQuSXln5reiqtIdaJzIakctW9fs3omlsZr8j7pXHvZEPQAyYnH2u396tXQxZLXSfdj68q5odUEXUDt6kxN70h3ikL/4gbkfpPkW24wd4NlVPA21GZR+rBpvfpZN1u57Lvp8Lm/QvUfTlka4H5"
|
||||
JANICE_KEY="encrypted:BFEsjnnZNfYFIXvGXKVtko5c8zh5sZze7hjFORfAb4QsHqHh/SqXVKClMCyEa8OMCjNtd8Zmz8LOckaOUYAh09Xi57KM6Eh33CirHipys0rdeURcwSkI9RSXPZOvmOfKZ9yDmhd3iov3AF6b+wwDQ9/rhYZrqh/NWETeHV98Xgv1"
|
||||
PERPLEXITY_API_KEY="encrypted:BIRn8UX4BgL/4QOCaz2cNZVfiJY3zR/Qclr2UI8FnDsUR8mu+hWi8SVWeaauzLSRwiWU1Ihc3/sWUxi8Jz/Ma1dGcDdPwPO7kiZuN2a1Tl3NKiSMmDlNYszLekhpESoUzVOa/605lcKsTemqC8SvVfq7rOPuC2QP7/7bIGSPGDShIfBjU7dUpvfpDX0/Vf++kLHkNiV1"
|
||||
@@ -1,8 +0,0 @@
|
||||
trailingComma: all
|
||||
tabWidth: 2
|
||||
useTabs: false
|
||||
semi: true
|
||||
singleQuote: true
|
||||
printWidth: 140
|
||||
experimentalTernaries: true
|
||||
quoteProps: consistent
|
||||
@@ -1,83 +0,0 @@
|
||||
# Star Kitten Web
|
||||
|
||||
Project created with [Brisa](https://github.com/brisa-build/brisa).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
### Link the Library
|
||||
|
||||
`star-kitten-lib` has not been published, so link to it locally before running this web project.
|
||||
|
||||
```bash
|
||||
cd star-kitten-lib
|
||||
bun link
|
||||
cd ../web
|
||||
bun link star-kitten-lib
|
||||
```
|
||||
|
||||
### Download static eve reference data & Hoboleaks archive from [EVE Ref](https://everef.net/).
|
||||
|
||||
```bash
|
||||
cd star-kitten-lib
|
||||
bun get-data
|
||||
```
|
||||
|
||||
### Initialize the sqlite database
|
||||
|
||||
```bash
|
||||
cd star-kitten-lib
|
||||
bun generate-migrations
|
||||
bun migrate
|
||||
```
|
||||
Drizzle's migrations seems to fail on the first try sometimes, so just grab the .sql from the generation and run those against the kitten.db file to create the tables & indexes.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Create a .env file in the root directory with the following values:
|
||||
|
||||
```yaml
|
||||
#General
|
||||
BASE_URL=http://localhost:3000
|
||||
DEBUG=true
|
||||
PORT=3000
|
||||
NODE_ENV=development
|
||||
LOG_LEVEL=debug
|
||||
|
||||
|
||||
# EVE - https://developers.eveonline.com/applications
|
||||
EVE_CLIENT_ID=YOUR_EVE_CLIENT_ID
|
||||
EVE_CLIENT_SECRET=YOUR_EVE_SECRET
|
||||
EVE_CALLBACK_URL=http://localhost:3000/auth/callback
|
||||
ESI_USER_AGENT=ADD_YOUR_USER_AGENT_INFO_HERE
|
||||
|
||||
# For using Janice's Appraisal API
|
||||
JANICE_KEY=XXX
|
||||
|
||||
# For using Perplexities AI API
|
||||
PERPLEXITY_API_KEY=XXX
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
bun dev
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
bun build
|
||||
```
|
||||
|
||||
### Start
|
||||
|
||||
```bash
|
||||
bun start
|
||||
```
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { Configuration } from "brisa";
|
||||
import tailwindcss from 'brisa-tailwindcss';
|
||||
|
||||
export default {
|
||||
integrations: [tailwindcss()],
|
||||
} as Configuration;
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface IntrinsicCustomElements {
|
||||
'counter-client': JSX.WebComponentAttributes<typeof import("D:\dev\@star-kitten\packages\eve-web\src\web-components\counter-client.tsx").default>;
|
||||
}
|
||||
export type PageRoute = "/" | "/about" | "/auth/error" | "/auth/success";
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,55 +0,0 @@
|
||||
// @bun
|
||||
var __create = Object.create;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __toESM = (mod, isNodeMode, target) => {
|
||||
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
||||
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
if (!__hasOwnProp.call(to, key))
|
||||
__defProp(to, key, {
|
||||
get: () => mod[key],
|
||||
enumerable: true
|
||||
});
|
||||
return to;
|
||||
};
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
var __require = import.meta.require;
|
||||
|
||||
// src/utils/cookies.ts
|
||||
function getCookies(headers) {
|
||||
if (!headers)
|
||||
return {};
|
||||
const cookieHeader = headers.get("Cookie");
|
||||
const cookies = {};
|
||||
if (cookieHeader === null)
|
||||
return {};
|
||||
for (const kv of cookieHeader.split(";")) {
|
||||
const [cookieKey, ...cookieVal] = kv.split("=");
|
||||
const key = cookieKey.trim();
|
||||
cookies[key] = cookieVal.join("=");
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
function setCookie(response, key, value, maxAge) {
|
||||
response.headers.append("Set-Cookie", `${key}=${value}${maxAge ? "; Path=/; Max-Age=" + maxAge : ""}`);
|
||||
}
|
||||
function removeCookie(response, key) {
|
||||
response.headers.append("Set-Cookie", `${key}=""; Path=/; Max-Age=-1;`);
|
||||
}
|
||||
// src/api/auth/discordID/[discordID]/characterID/[characterID]/scopes/[scopes].ts
|
||||
async function GET({ store, route: { params } }) {
|
||||
const eveauth = store.get("eveauth");
|
||||
const response = await eveauth.redirect(params["scopes"]);
|
||||
setCookie(response, "discordID", params["discordID"], 60 * 10);
|
||||
setCookie(response, "characterID", params["characterID"], 60 * 10);
|
||||
return response;
|
||||
}
|
||||
export {
|
||||
GET
|
||||
};
|
||||
|
||||
//# debugId=AEE66F1C816CC1AD64756E2164756E21
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi5cXHNyY1xcdXRpbHNcXGNvb2tpZXMudHMiLCAiLi5cXHNyY1xcYXBpXFxhdXRoXFxkaXNjb3JkSURcXFtkaXNjb3JkSURdXFxjaGFyYWN0ZXJJRFxcW2NoYXJhY3RlcklEXVxcc2NvcGVzXFxbc2NvcGVzXS50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDb29raWVzKGhlYWRlcnM6IEhlYWRlcnMpIHtcbiAgaWYgKCFoZWFkZXJzKSByZXR1cm4ge307XG4gIGNvbnN0IGNvb2tpZUhlYWRlciA9IGhlYWRlcnMuZ2V0KFwiQ29va2llXCIpO1xuICBjb25zdCBjb29raWVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgaWYgKGNvb2tpZUhlYWRlciA9PT0gbnVsbCkgcmV0dXJuIHt9O1xuXG4gIGZvciAoY29uc3Qga3Ygb2YgY29va2llSGVhZGVyLnNwbGl0KFwiO1wiKSkge1xuICAgIGNvbnN0IFtjb29raWVLZXksIC4uLmNvb2tpZVZhbF0gPSBrdi5zcGxpdChcIj1cIik7XG4gICAgY29uc3Qga2V5ID0gY29va2llS2V5LnRyaW0oKTtcbiAgICBjb29raWVzW2tleV0gPSBjb29raWVWYWwuam9pbihcIj1cIik7XG4gIH1cblxuICByZXR1cm4gY29va2llcztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldENvb2tpZShyZXNwb25zZTogUmVzcG9uc2UsIGtleTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nLCBtYXhBZ2U/OiBudW1iZXIpIHtcbiAgcmVzcG9uc2UuaGVhZGVycy5hcHBlbmQoJ1NldC1Db29raWUnLCBgJHtrZXl9PSR7dmFsdWV9JHttYXhBZ2UgPyAnOyBQYXRoPS87IE1heC1BZ2U9JyArIG1heEFnZSA6ICcnfWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVtb3ZlQ29va2llKHJlc3BvbnNlOiBSZXNwb25zZSwga2V5OiBzdHJpbmcpIHtcbiAgcmVzcG9uc2UuaGVhZGVycy5hcHBlbmQoJ1NldC1Db29raWUnLCBgJHtrZXl9PVwiXCI7IFBhdGg9LzsgTWF4LUFnZT0tMTtgKTtcbn0iLAogICAgImltcG9ydCB0eXBlIHsgRVZFQXV0aCB9IGZyb20gJ0AvbWlkZGxld2FyZSc7XG5pbXBvcnQgeyBzZXRDb29raWUgfSBmcm9tICdAL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgUmVxdWVzdENvbnRleHQgfSBmcm9tICdicmlzYSc7XG5cbi8vIEdFVCAvYXBpL2F1dGgvZGlzY29yZElELzpkaXNjb3JkSUQvY2hhcmFjdGVySUQvOmNoYXJhY3RlcklEL3Njb3Blcy86c2NvcGVzXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gR0VUKHsgc3RvcmUsIHJvdXRlOiB7IHBhcmFtcyB9IH06IFJlcXVlc3RDb250ZXh0KSB7XG4gIC8vIHRoaXMgaXMgdXNlZCB0byBzZXQgdGhlIHNjb3BlcyB0aGF0IHdlcmUgc2VudCwgc28ganVzdCBwYXNzIHRoZW0gYWxvbmcgdG8gYXV0aCBkaXJlY3RseSBcbiAgLy8gd2l0aCB0aGUgcHJvdmlkZWQgc2NvcGVzXG4gIGNvbnN0IGV2ZWF1dGg6IEVWRUF1dGggPSBzdG9yZS5nZXQoJ2V2ZWF1dGgnKTtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBldmVhdXRoLnJlZGlyZWN0KHBhcmFtcyFbJ3Njb3BlcyddIGFzIHN0cmluZyk7XG4gIHNldENvb2tpZShyZXNwb25zZSwgJ2Rpc2NvcmRJRCcsIHBhcmFtcyFbJ2Rpc2NvcmRJRCddIGFzIHN0cmluZywgNjAgKiAxMCAvKiAxMCBtaW4gKi8pO1xuICBzZXRDb29raWUocmVzcG9uc2UsICdjaGFyYWN0ZXJJRCcsIHBhcmFtcyFbJ2NoYXJhY3RlcklEJ10gYXMgc3RyaW5nLCA2MCAqIDEwIC8qIDEwIG1pbiAqLyk7XG4gIHJldHVybiByZXNwb25zZTtcbn0iCiAgXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFDTyxTQUFTLFVBQVUsQ0FBQyxTQUFrQjtBQUFBLEVBQzNDLEtBQUs7QUFBQSxJQUFTLE9BQU8sQ0FBQztBQUFBLEVBQ3RCLE1BQU0sZUFBZSxRQUFRLElBQUksUUFBUTtBQUFBLEVBQ3pDLE1BQU0sVUFBa0MsQ0FBQztBQUFBLEVBRXpDLElBQUksaUJBQWlCO0FBQUEsSUFBTSxPQUFPLENBQUM7QUFBQSxFQUVuQyxXQUFXLE1BQU0sYUFBYSxNQUFNLEdBQUcsR0FBRztBQUFBLElBQ3hDLE9BQU8sY0FBYyxhQUFhLEdBQUcsTUFBTSxHQUFHO0FBQUEsSUFDOUMsTUFBTSxNQUFNLFVBQVUsS0FBSztBQUFBLElBQzNCLFFBQVEsT0FBTyxVQUFVLEtBQUssR0FBRztBQUFBLEVBQ25DO0FBQUEsRUFFQSxPQUFPO0FBQUE7QUFHRixTQUFTLFNBQVMsQ0FBQyxVQUFvQixLQUFhLE9BQWUsUUFBaUI7QUFBQSxFQUN6RixTQUFTLFFBQVEsT0FBTyxjQUFjLEdBQUcsT0FBTyxRQUFRLFNBQVMsdUJBQXVCLFNBQVMsSUFBSTtBQUFBO0FBR2hHLFNBQVMsWUFBWSxDQUFDLFVBQW9CLEtBQWE7QUFBQSxFQUM1RCxTQUFTLFFBQVEsT0FBTyxjQUFjLEdBQUcsNkJBQTZCO0FBQUE7O0FDakJ4RSxlQUFzQixHQUFHLEdBQUcsT0FBTyxTQUFTLFlBQTRCO0FBQUEsRUFHdEUsTUFBTSxVQUFtQixNQUFNLElBQUksU0FBUztBQUFBLEVBQzVDLE1BQU0sV0FBVyxNQUFNLFFBQVEsU0FBUyxPQUFRLFNBQW1CO0FBQUEsRUFDbkUsVUFBVSxVQUFVLGFBQWEsT0FBUSxjQUF3QixLQUFLLEVBQWU7QUFBQSxFQUNyRixVQUFVLFVBQVUsZUFBZSxPQUFRLGdCQUEwQixLQUFLLEVBQWU7QUFBQSxFQUN6RixPQUFPO0FBQUE7IiwKICAiZGVidWdJZCI6ICJBRUU2NkYxQzgxNkNDMUFENjQ3NTZFMjE2NDc1NkUyMSIsCiAgIm5hbWVzIjogW10KfQ==
|
||||
@@ -1,54 +0,0 @@
|
||||
// @bun
|
||||
var __create = Object.create;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __toESM = (mod, isNodeMode, target) => {
|
||||
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
||||
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
if (!__hasOwnProp.call(to, key))
|
||||
__defProp(to, key, {
|
||||
get: () => mod[key],
|
||||
enumerable: true
|
||||
});
|
||||
return to;
|
||||
};
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
var __require = import.meta.require;
|
||||
|
||||
// src/utils/cookies.ts
|
||||
function getCookies(headers) {
|
||||
if (!headers)
|
||||
return {};
|
||||
const cookieHeader = headers.get("Cookie");
|
||||
const cookies = {};
|
||||
if (cookieHeader === null)
|
||||
return {};
|
||||
for (const kv of cookieHeader.split(";")) {
|
||||
const [cookieKey, ...cookieVal] = kv.split("=");
|
||||
const key = cookieKey.trim();
|
||||
cookies[key] = cookieVal.join("=");
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
function setCookie(response, key, value, maxAge) {
|
||||
response.headers.append("Set-Cookie", `${key}=${value}${maxAge ? "; Path=/; Max-Age=" + maxAge : ""}`);
|
||||
}
|
||||
function removeCookie(response, key) {
|
||||
response.headers.append("Set-Cookie", `${key}=""; Path=/; Max-Age=-1;`);
|
||||
}
|
||||
// src/api/auth/discordID/[discordID]/index.ts
|
||||
async function GET({ store, route: { params } }) {
|
||||
const eveauth = store.get("eveauth");
|
||||
const response = await eveauth.redirect("publicData");
|
||||
setCookie(response, "discordID", params["discordID"], 60 * 10);
|
||||
return response;
|
||||
}
|
||||
export {
|
||||
GET
|
||||
};
|
||||
|
||||
//# debugId=7AF70B8658B4D3D064756E2164756E21
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi5cXHNyY1xcdXRpbHNcXGNvb2tpZXMudHMiLCAiLi5cXHNyY1xcYXBpXFxhdXRoXFxkaXNjb3JkSURcXFtkaXNjb3JkSURdXFxpbmRleC50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDb29raWVzKGhlYWRlcnM6IEhlYWRlcnMpIHtcbiAgaWYgKCFoZWFkZXJzKSByZXR1cm4ge307XG4gIGNvbnN0IGNvb2tpZUhlYWRlciA9IGhlYWRlcnMuZ2V0KFwiQ29va2llXCIpO1xuICBjb25zdCBjb29raWVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgaWYgKGNvb2tpZUhlYWRlciA9PT0gbnVsbCkgcmV0dXJuIHt9O1xuXG4gIGZvciAoY29uc3Qga3Ygb2YgY29va2llSGVhZGVyLnNwbGl0KFwiO1wiKSkge1xuICAgIGNvbnN0IFtjb29raWVLZXksIC4uLmNvb2tpZVZhbF0gPSBrdi5zcGxpdChcIj1cIik7XG4gICAgY29uc3Qga2V5ID0gY29va2llS2V5LnRyaW0oKTtcbiAgICBjb29raWVzW2tleV0gPSBjb29raWVWYWwuam9pbihcIj1cIik7XG4gIH1cblxuICByZXR1cm4gY29va2llcztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldENvb2tpZShyZXNwb25zZTogUmVzcG9uc2UsIGtleTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nLCBtYXhBZ2U/OiBudW1iZXIpIHtcbiAgcmVzcG9uc2UuaGVhZGVycy5hcHBlbmQoJ1NldC1Db29raWUnLCBgJHtrZXl9PSR7dmFsdWV9JHttYXhBZ2UgPyAnOyBQYXRoPS87IE1heC1BZ2U9JyArIG1heEFnZSA6ICcnfWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVtb3ZlQ29va2llKHJlc3BvbnNlOiBSZXNwb25zZSwga2V5OiBzdHJpbmcpIHtcbiAgcmVzcG9uc2UuaGVhZGVycy5hcHBlbmQoJ1NldC1Db29raWUnLCBgJHtrZXl9PVwiXCI7IFBhdGg9LzsgTWF4LUFnZT0tMTtgKTtcbn0iLAogICAgImltcG9ydCB0eXBlIHsgRVZFQXV0aCB9IGZyb20gJ0AvbWlkZGxld2FyZSc7XG5pbXBvcnQgeyBzZXRDb29raWUgfSBmcm9tICdAL3V0aWxzJztcbmltcG9ydCB0eXBlIHsgUmVxdWVzdENvbnRleHQgfSBmcm9tICdicmlzYSc7XG5cbi8vIEdFVCAvYXBpL2F1dGgvZGlzY29yZElELzpkaXNjb3JkSURcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBHRVQoeyBzdG9yZSwgcm91dGU6IHsgcGFyYW1zIH19OiBSZXF1ZXN0Q29udGV4dCkge1xuICAvLyBjYWxsZWQgd2hlbiBhZGRpbmcgYSBuZXcgY2hhcmFjdGVyLCBzbyBqdXN0IHJlZGlyZWN0IHRvIGF1dGggYW5kIHNldCBjb29raWVzXG4gIGNvbnN0IGV2ZWF1dGg6IEVWRUF1dGggPSAgc3RvcmUuZ2V0KCdldmVhdXRoJyk7XG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZXZlYXV0aC5yZWRpcmVjdCgncHVibGljRGF0YScpO1xuICBzZXRDb29raWUocmVzcG9uc2UsICdkaXNjb3JkSUQnLCBwYXJhbXMhWydkaXNjb3JkSUQnXSBhcyBzdHJpbmcsIDYwICogMTAgLyogMTAgbWluICovKTtcbiAgcmV0dXJuIHJlc3BvbnNlO1xufSIKICBdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUNPLFNBQVMsVUFBVSxDQUFDLFNBQWtCO0FBQUEsRUFDM0MsS0FBSztBQUFBLElBQVMsT0FBTyxDQUFDO0FBQUEsRUFDdEIsTUFBTSxlQUFlLFFBQVEsSUFBSSxRQUFRO0FBQUEsRUFDekMsTUFBTSxVQUFrQyxDQUFDO0FBQUEsRUFFekMsSUFBSSxpQkFBaUI7QUFBQSxJQUFNLE9BQU8sQ0FBQztBQUFBLEVBRW5DLFdBQVcsTUFBTSxhQUFhLE1BQU0sR0FBRyxHQUFHO0FBQUEsSUFDeEMsT0FBTyxjQUFjLGFBQWEsR0FBRyxNQUFNLEdBQUc7QUFBQSxJQUM5QyxNQUFNLE1BQU0sVUFBVSxLQUFLO0FBQUEsSUFDM0IsUUFBUSxPQUFPLFVBQVUsS0FBSyxHQUFHO0FBQUEsRUFDbkM7QUFBQSxFQUVBLE9BQU87QUFBQTtBQUdGLFNBQVMsU0FBUyxDQUFDLFVBQW9CLEtBQWEsT0FBZSxRQUFpQjtBQUFBLEVBQ3pGLFNBQVMsUUFBUSxPQUFPLGNBQWMsR0FBRyxPQUFPLFFBQVEsU0FBUyx1QkFBdUIsU0FBUyxJQUFJO0FBQUE7QUFHaEcsU0FBUyxZQUFZLENBQUMsVUFBb0IsS0FBYTtBQUFBLEVBQzVELFNBQVMsUUFBUSxPQUFPLGNBQWMsR0FBRyw2QkFBNkI7QUFBQTs7QUNqQnhFLGVBQXNCLEdBQUcsR0FBRyxPQUFPLFNBQVMsWUFBMkI7QUFBQSxFQUVyRSxNQUFNLFVBQW9CLE1BQU0sSUFBSSxTQUFTO0FBQUEsRUFDN0MsTUFBTSxXQUFXLE1BQU0sUUFBUSxTQUFTLFlBQVk7QUFBQSxFQUNwRCxVQUFVLFVBQVUsYUFBYSxPQUFRLGNBQXdCLEtBQUssRUFBZTtBQUFBLEVBQ3JGLE9BQU87QUFBQTsiLAogICJkZWJ1Z0lkIjogIjdBRjcwQjg2NThCNEQzRDA2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
|
||||
File diff suppressed because one or more lines are too long
@@ -1,31 +0,0 @@
|
||||
// @bun
|
||||
var __create = Object.create;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __toESM = (mod, isNodeMode, target) => {
|
||||
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
||||
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
if (!__hasOwnProp.call(to, key))
|
||||
__defProp(to, key, {
|
||||
get: () => mod[key],
|
||||
enumerable: true
|
||||
});
|
||||
return to;
|
||||
};
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
var __require = import.meta.require;
|
||||
|
||||
// src/api/auth/index.ts
|
||||
function GET(request) {
|
||||
const eveauth = request.store.get("eveauth");
|
||||
return eveauth.redirect();
|
||||
}
|
||||
export {
|
||||
GET
|
||||
};
|
||||
|
||||
//# debugId=2B9DFE71D16A520B64756E2164756E21
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi5cXHNyY1xcYXBpXFxhdXRoXFxpbmRleC50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJpbXBvcnQgdHlwZSB7IFJlcXVlc3RDb250ZXh0IH0gZnJvbSAnYnJpc2EnO1xuaW1wb3J0IHR5cGUgeyBFVkVBdXRoIH0gZnJvbSAnQC9taWRkbGV3YXJlJztcblxuLy8gR0VUIC9hcGkvYXV0aC9cbmV4cG9ydCBmdW5jdGlvbiBHRVQocmVxdWVzdDogUmVxdWVzdENvbnRleHQpIHtcbiAgY29uc3QgZXZlYXV0aDogRVZFQXV0aCA9ICByZXF1ZXN0LnN0b3JlLmdldCgnZXZlYXV0aCcpO1xuICByZXR1cm4gZXZlYXV0aC5yZWRpcmVjdCgpO1xufSIKICBdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUlPLFNBQVMsR0FBRyxDQUFDLFNBQXlCO0FBQUEsRUFDM0MsTUFBTSxVQUFvQixRQUFRLE1BQU0sSUFBSSxTQUFTO0FBQUEsRUFDckQsT0FBTyxRQUFRLFNBQVM7QUFBQTsiLAogICJkZWJ1Z0lkIjogIjJCOURGRTcxRDE2QTUyMEI2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
|
||||
@@ -1 +0,0 @@
|
||||
export default ["style-4253422825010316650.css","style-4799912372787954844.css","style-16814944961881043919.css"]
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
l$=new Set;u$=(k)=>{let g=(f)=>document.getElementById(f);l$.add(k);for(let f of l$){let j=g(`S:${f}`),h=g(`U:${f}`);if(j&&h)l$.delete(f),j.replaceWith(h.content.cloneNode(!0)),h.remove(),g(`R:${f}`)?.remove()}};
|
||||
@@ -1 +0,0 @@
|
||||
\pages\index.js
|
||||
File diff suppressed because one or more lines are too long
@@ -1,56 +0,0 @@
|
||||
// @bun
|
||||
var __create = Object.create;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __toESM = (mod, isNodeMode, target) => {
|
||||
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
||||
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
if (!__hasOwnProp.call(to, key))
|
||||
__defProp(to, key, {
|
||||
get: () => mod[key],
|
||||
enumerable: true
|
||||
});
|
||||
return to;
|
||||
};
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
var __require = import.meta.require;
|
||||
|
||||
// node_modules/brisa/jsx-runtime/index.js
|
||||
var n = Symbol.for("isJSX");
|
||||
function S(r) {
|
||||
return i(null, r);
|
||||
}
|
||||
function i(r, { children: e, ...o }, s) {
|
||||
let a = e;
|
||||
if (Array.isArray(e) && !l(e))
|
||||
a = e.map((t) => t?.[n] ? t : S({ children: t }));
|
||||
return Object.assign([r, { ...o, key: s }, a], { [n]: true });
|
||||
}
|
||||
function l(r) {
|
||||
return Array.isArray(r) && ((n in r) || m(r));
|
||||
}
|
||||
function m(r) {
|
||||
return r?.[0] === "HTML" && typeof r[1]?.html === "string";
|
||||
}
|
||||
|
||||
// src/pages/auth/error.tsx
|
||||
function Error() {
|
||||
return i(S, {
|
||||
children: i("div", {
|
||||
children: [i("h1", {
|
||||
children: "EVE auth failed, close this tab and try again."
|
||||
}, undefined, false, undefined, this), i("p", {
|
||||
children: "If this issue persists reach out to an Administrator."
|
||||
}, undefined, false, undefined, this)]
|
||||
}, undefined, true, undefined, this)
|
||||
}, undefined, false, undefined, this);
|
||||
}
|
||||
export {
|
||||
Error as default
|
||||
};
|
||||
|
||||
//# debugId=F0E3E3449C64DA0864756E2164756E21
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi5cXG5vZGVfbW9kdWxlc1xcYnJpc2FcXGpzeC1ydW50aW1lXFxpbmRleC5qcyIsICIuLlxcc3JjXFxwYWdlc1xcYXV0aFxcZXJyb3IudHN4Il0sCiAgInNvdXJjZXNDb250ZW50IjogWwogICAgIi8vIEBidW5cbnZhciBuPVN5bWJvbC5mb3IoXCJpc0pTWFwiKTtmdW5jdGlvbiBTKHIpe3JldHVybiBpKG51bGwscil9ZnVuY3Rpb24gaShyLHtjaGlsZHJlbjplLC4uLm99LHMpe2xldCBhPWU7aWYoQXJyYXkuaXNBcnJheShlKSYmIWwoZSkpYT1lLm1hcCgodCk9PnQ/LltuXT90OlMoe2NoaWxkcmVuOnR9KSk7cmV0dXJuIE9iamVjdC5hc3NpZ24oW3Isey4uLm8sa2V5OnN9LGFdLHtbbl06ITB9KX1mdW5jdGlvbiBsKHIpe3JldHVybiBBcnJheS5pc0FycmF5KHIpJiYoKG4gaW4gcil8fG0ocikpfWZ1bmN0aW9uIG0ocil7cmV0dXJuIHI/LlswXT09PVwiSFRNTFwiJiZ0eXBlb2YgclsxXT8uaHRtbD09PVwic3RyaW5nXCJ9ZXhwb3J0e2kgYXMganN4cyxpIGFzIGpzeERFVixpIGFzIGpzeCxtIGFzIGlzRGFuZ2VySFRNTCxsIGFzIGlzQXJyYXdPZkpTWENvbnRlbnQsUyBhcyBGcmFnbWVudH07XG4iLAogICAgImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEVycm9yKCkge1xuICByZXR1cm4ganN4REVWXzd4ODFoMGtuKEZyYWdtZW50Xzh2Zzl4M3NxLCB7XG4gICAgY2hpbGRyZW46IGpzeERFVl83eDgxaDBrbihcImRpdlwiLCB7XG4gICAgICBjaGlsZHJlbjogW2pzeERFVl83eDgxaDBrbihcImgxXCIsIHtcbiAgICAgICAgY2hpbGRyZW46IFwiRVZFIGF1dGggZmFpbGVkLCBjbG9zZSB0aGlzIHRhYiBhbmQgdHJ5IGFnYWluLlwiXG4gICAgICB9LCB1bmRlZmluZWQsIGZhbHNlLCB1bmRlZmluZWQsIHRoaXMpLCBqc3hERVZfN3g4MWgwa24oXCJwXCIsIHtcbiAgICAgICAgY2hpbGRyZW46IFwiSWYgdGhpcyBpc3N1ZSBwZXJzaXN0cyByZWFjaCBvdXQgdG8gYW4gQWRtaW5pc3RyYXRvci5cIlxuICAgICAgfSwgdW5kZWZpbmVkLCBmYWxzZSwgdW5kZWZpbmVkLCB0aGlzKV1cbiAgICB9LCB1bmRlZmluZWQsIHRydWUsIHVuZGVmaW5lZCwgdGhpcylcbiAgfSwgdW5kZWZpbmVkLCBmYWxzZSwgdW5kZWZpbmVkLCB0aGlzKTtcbn1cbmltcG9ydCB7IGpzeCBhcyBqc3hfdzc3eWFmczQsIGpzeHMgYXMganN4c19laDZjNzhuaiwganN4REVWIGFzIGpzeERFVl83eDgxaDBrbiwgRnJhZ21lbnQgYXMgRnJhZ21lbnRfOHZnOXgzc3EgfSBmcm9tICdicmlzYS9qc3gtcnVudGltZSc7XG4iCiAgXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFDQSxJQUFJLElBQUUsT0FBTyxJQUFJLE9BQU87QUFBRSxTQUFTLENBQUMsQ0FBQyxHQUFFO0FBQUEsRUFBQyxPQUFPLEVBQUUsTUFBSyxDQUFDO0FBQUE7QUFBRSxTQUFTLENBQUMsQ0FBQyxLQUFHLFVBQVMsTUFBSyxLQUFHLEdBQUU7QUFBQSxFQUFDLElBQUksSUFBRTtBQUFBLEVBQUUsSUFBRyxNQUFNLFFBQVEsQ0FBQyxNQUFJLEVBQUUsQ0FBQztBQUFBLElBQUUsSUFBRSxFQUFFLElBQUksQ0FBQyxNQUFJLElBQUksS0FBRyxJQUFFLEVBQUUsRUFBQyxVQUFTLEVBQUMsQ0FBQyxDQUFDO0FBQUEsRUFBRSxPQUFPLE9BQU8sT0FBTyxDQUFDLEdBQUUsS0FBSSxHQUFFLEtBQUksRUFBQyxHQUFFLENBQUMsR0FBRSxHQUFFLElBQUcsS0FBRSxDQUFDO0FBQUE7QUFBRSxTQUFTLENBQUMsQ0FBQyxHQUFFO0FBQUEsRUFBQyxPQUFPLE1BQU0sUUFBUSxDQUFDLE9BQUssS0FBSyxNQUFJLEVBQUUsQ0FBQztBQUFBO0FBQUcsU0FBUyxDQUFDLENBQUMsR0FBRTtBQUFBLEVBQUMsT0FBTyxJQUFJLE9BQUssVUFBUSxPQUFPLEVBQUUsSUFBSSxTQUFPO0FBQUE7OztBQ0R6VSxTQUF3QixLQUFLLEdBQUc7QUFBQSxFQUM5QixPQUFPLEVBQWdCLEdBQW1CO0FBQUEsSUFDeEMsVUFBVSxFQUFnQixPQUFPO0FBQUEsTUFDL0IsVUFBVSxDQUFDLEVBQWdCLE1BQU07QUFBQSxRQUMvQixVQUFVO0FBQUEsTUFDWixHQUFHLFdBQVcsT0FBTyxXQUFXLElBQUksR0FBRyxFQUFnQixLQUFLO0FBQUEsUUFDMUQsVUFBVTtBQUFBLE1BQ1osR0FBRyxXQUFXLE9BQU8sV0FBVyxJQUFJLENBQUM7QUFBQSxJQUN2QyxHQUFHLFdBQVcsTUFBTSxXQUFXLElBQUk7QUFBQSxFQUNyQyxHQUFHLFdBQVcsT0FBTyxXQUFXLElBQUk7QUFBQTsiLAogICJkZWJ1Z0lkIjogIkYwRTNFMzQ0OUM2NERBMDg2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
|
||||
@@ -1,55 +0,0 @@
|
||||
// @bun
|
||||
var __create = Object.create;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __toESM = (mod, isNodeMode, target) => {
|
||||
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
||||
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
if (!__hasOwnProp.call(to, key))
|
||||
__defProp(to, key, {
|
||||
get: () => mod[key],
|
||||
enumerable: true
|
||||
});
|
||||
return to;
|
||||
};
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
var __require = import.meta.require;
|
||||
|
||||
// node_modules/brisa/jsx-runtime/index.js
|
||||
var n = Symbol.for("isJSX");
|
||||
function S(r) {
|
||||
return i(null, r);
|
||||
}
|
||||
function i(r, { children: e, ...o }, s) {
|
||||
let a = e;
|
||||
if (Array.isArray(e) && !l(e))
|
||||
a = e.map((t) => t?.[n] ? t : S({ children: t }));
|
||||
return Object.assign([r, { ...o, key: s }, a], { [n]: true });
|
||||
}
|
||||
function l(r) {
|
||||
return Array.isArray(r) && ((n in r) || m(r));
|
||||
}
|
||||
function m(r) {
|
||||
return r?.[0] === "HTML" && typeof r[1]?.html === "string";
|
||||
}
|
||||
|
||||
// src/pages/auth/success.tsx
|
||||
function Success() {
|
||||
return i(S, {
|
||||
children: i("div", {
|
||||
class: "hero",
|
||||
children: i("h1", {
|
||||
children: "EVE auth success, you may close this browser tab."
|
||||
}, undefined, false, undefined, this)
|
||||
}, undefined, false, undefined, this)
|
||||
}, undefined, false, undefined, this);
|
||||
}
|
||||
export {
|
||||
Success as default
|
||||
};
|
||||
|
||||
//# debugId=DE19EB2F6E2E7A6A64756E2164756E21
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi5cXG5vZGVfbW9kdWxlc1xcYnJpc2FcXGpzeC1ydW50aW1lXFxpbmRleC5qcyIsICIuLlxcc3JjXFxwYWdlc1xcYXV0aFxcc3VjY2Vzcy50c3giXSwKICAic291cmNlc0NvbnRlbnQiOiBbCiAgICAiLy8gQGJ1blxudmFyIG49U3ltYm9sLmZvcihcImlzSlNYXCIpO2Z1bmN0aW9uIFMocil7cmV0dXJuIGkobnVsbCxyKX1mdW5jdGlvbiBpKHIse2NoaWxkcmVuOmUsLi4ub30scyl7bGV0IGE9ZTtpZihBcnJheS5pc0FycmF5KGUpJiYhbChlKSlhPWUubWFwKCh0KT0+dD8uW25dP3Q6Uyh7Y2hpbGRyZW46dH0pKTtyZXR1cm4gT2JqZWN0LmFzc2lnbihbcix7Li4ubyxrZXk6c30sYV0se1tuXTohMH0pfWZ1bmN0aW9uIGwocil7cmV0dXJuIEFycmF5LmlzQXJyYXkocikmJigobiBpbiByKXx8bShyKSl9ZnVuY3Rpb24gbShyKXtyZXR1cm4gcj8uWzBdPT09XCJIVE1MXCImJnR5cGVvZiByWzFdPy5odG1sPT09XCJzdHJpbmdcIn1leHBvcnR7aSBhcyBqc3hzLGkgYXMganN4REVWLGkgYXMganN4LG0gYXMgaXNEYW5nZXJIVE1MLGwgYXMgaXNBcnJhd09mSlNYQ29udGVudCxTIGFzIEZyYWdtZW50fTtcbiIsCiAgICAiZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gU3VjY2VzcygpIHtcbiAgcmV0dXJuIGpzeERFVl83eDgxaDBrbihGcmFnbWVudF84dmc5eDNzcSwge1xuICAgIGNoaWxkcmVuOiBqc3hERVZfN3g4MWgwa24oXCJkaXZcIiwge1xuICAgICAgY2xhc3M6IFwiaGVyb1wiLFxuICAgICAgY2hpbGRyZW46IGpzeERFVl83eDgxaDBrbihcImgxXCIsIHtcbiAgICAgICAgY2hpbGRyZW46IFwiRVZFIGF1dGggc3VjY2VzcywgeW91IG1heSBjbG9zZSB0aGlzIGJyb3dzZXIgdGFiLlwiXG4gICAgICB9LCB1bmRlZmluZWQsIGZhbHNlLCB1bmRlZmluZWQsIHRoaXMpXG4gICAgfSwgdW5kZWZpbmVkLCBmYWxzZSwgdW5kZWZpbmVkLCB0aGlzKVxuICB9LCB1bmRlZmluZWQsIGZhbHNlLCB1bmRlZmluZWQsIHRoaXMpO1xufVxuaW1wb3J0IHsganN4IGFzIGpzeF93Nzd5YWZzNCwganN4cyBhcyBqc3hzX2VoNmM3OG5qLCBqc3hERVYgYXMganN4REVWXzd4ODFoMGtuLCBGcmFnbWVudCBhcyBGcmFnbWVudF84dmc5eDNzcSB9IGZyb20gJ2JyaXNhL2pzeC1ydW50aW1lJztcbiIKICBdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUNBLElBQUksSUFBRSxPQUFPLElBQUksT0FBTztBQUFFLFNBQVMsQ0FBQyxDQUFDLEdBQUU7QUFBQSxFQUFDLE9BQU8sRUFBRSxNQUFLLENBQUM7QUFBQTtBQUFFLFNBQVMsQ0FBQyxDQUFDLEtBQUcsVUFBUyxNQUFLLEtBQUcsR0FBRTtBQUFBLEVBQUMsSUFBSSxJQUFFO0FBQUEsRUFBRSxJQUFHLE1BQU0sUUFBUSxDQUFDLE1BQUksRUFBRSxDQUFDO0FBQUEsSUFBRSxJQUFFLEVBQUUsSUFBSSxDQUFDLE1BQUksSUFBSSxLQUFHLElBQUUsRUFBRSxFQUFDLFVBQVMsRUFBQyxDQUFDLENBQUM7QUFBQSxFQUFFLE9BQU8sT0FBTyxPQUFPLENBQUMsR0FBRSxLQUFJLEdBQUUsS0FBSSxFQUFDLEdBQUUsQ0FBQyxHQUFFLEdBQUUsSUFBRyxLQUFFLENBQUM7QUFBQTtBQUFFLFNBQVMsQ0FBQyxDQUFDLEdBQUU7QUFBQSxFQUFDLE9BQU8sTUFBTSxRQUFRLENBQUMsT0FBSyxLQUFLLE1BQUksRUFBRSxDQUFDO0FBQUE7QUFBRyxTQUFTLENBQUMsQ0FBQyxHQUFFO0FBQUEsRUFBQyxPQUFPLElBQUksT0FBSyxVQUFRLE9BQU8sRUFBRSxJQUFJLFNBQU87QUFBQTs7O0FDRHpVLFNBQXdCLE9BQU8sR0FBRztBQUFBLEVBQ2hDLE9BQU8sRUFBZ0IsR0FBbUI7QUFBQSxJQUN4QyxVQUFVLEVBQWdCLE9BQU87QUFBQSxNQUMvQixPQUFPO0FBQUEsTUFDUCxVQUFVLEVBQWdCLE1BQU07QUFBQSxRQUM5QixVQUFVO0FBQUEsTUFDWixHQUFHLFdBQVcsT0FBTyxXQUFXLElBQUk7QUFBQSxJQUN0QyxHQUFHLFdBQVcsT0FBTyxXQUFXLElBQUk7QUFBQSxFQUN0QyxHQUFHLFdBQVcsT0FBTyxXQUFXLElBQUk7QUFBQTsiLAogICJkZWJ1Z0lkIjogIkRFMTlFQjJGNkUyRTdBNkE2NDc1NkUyMTY0NzU2RTIxIiwKICAibmFtZXMiOiBbXQp9
|
||||
File diff suppressed because one or more lines are too long
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 200 199" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-1.46627e-05,-0.862671)">
|
||||
<path d="M124.615,158.942C194.326,134.262 190.916,42.893 140.767,0.863C184.318,20.349 206.756,72.952 198.203,118.905C191.512,154.854 164.558,184.522 129.69,194.524C111.34,199.788 91.695,200.819 73.085,196.201C36.436,187.108 7.653,155.954 1.303,118.778C-3.783,88.998 6.075,58.343 29.922,38.996C54.808,18.806 92.107,14.056 119.324,32.517C146.142,50.708 155.776,89.452 135.231,116.111C124.748,129.714 107.413,137.633 90.191,135.012C72.184,132.272 58.17,116.038 60.606,97.494C63.655,121.563 92.134,130.674 109.243,113.883C125.024,98.394 116.512,74.337 100.517,62.603C87.184,52.821 68.389,55.268 55.26,64.22C39.06,75.266 32.739,94.717 36.289,113.574C44.063,154.866 87.626,172.037 124.615,158.942L124.615,158.942Z" style="fill:url(#_Radial1);"/>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient id="_Radial1" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(52.186,-99.1373,99.1373,52.186,88.8208,100)"><stop offset="0" style="stop-color:rgb(44,240,204);stop-opacity:1"/><stop offset="0.53" style="stop-color:rgb(44,230,209);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(44,195,228);stop-opacity:1"/></radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
footer {
|
||||
display: grid;
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-size: 27px;
|
||||
line-height: 1.5;
|
||||
height: 15vh;
|
||||
place-items: center;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
/* Navigation Styles */
|
||||
@@ -1,329 +0,0 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "Star Kitten Web",
|
||||
"dependencies": {
|
||||
"brisa": "0.2.7",
|
||||
"brisa-tailwindcss": "0.2.7",
|
||||
"drizzle-orm": "^0.40.0",
|
||||
"oslo": "^1.2.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"daisyui": "^5.0.0",
|
||||
"drizzle-kit": "^0.30.5",
|
||||
"typescript": "latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
||||
|
||||
"@emnapi/core": ["@emnapi/core@0.45.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@0.45.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w=="],
|
||||
|
||||
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||
|
||||
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
||||
|
||||
"@node-rs/argon2": ["@node-rs/argon2@1.7.0", "", { "optionalDependencies": { "@node-rs/argon2-android-arm-eabi": "1.7.0", "@node-rs/argon2-android-arm64": "1.7.0", "@node-rs/argon2-darwin-arm64": "1.7.0", "@node-rs/argon2-darwin-x64": "1.7.0", "@node-rs/argon2-freebsd-x64": "1.7.0", "@node-rs/argon2-linux-arm-gnueabihf": "1.7.0", "@node-rs/argon2-linux-arm64-gnu": "1.7.0", "@node-rs/argon2-linux-arm64-musl": "1.7.0", "@node-rs/argon2-linux-x64-gnu": "1.7.0", "@node-rs/argon2-linux-x64-musl": "1.7.0", "@node-rs/argon2-wasm32-wasi": "1.7.0", "@node-rs/argon2-win32-arm64-msvc": "1.7.0", "@node-rs/argon2-win32-ia32-msvc": "1.7.0", "@node-rs/argon2-win32-x64-msvc": "1.7.0" } }, "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog=="],
|
||||
|
||||
"@node-rs/argon2-android-arm-eabi": ["@node-rs/argon2-android-arm-eabi@1.7.0", "", { "os": "android", "cpu": "arm" }, "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg=="],
|
||||
|
||||
"@node-rs/argon2-android-arm64": ["@node-rs/argon2-android-arm64@1.7.0", "", { "os": "android", "cpu": "arm64" }, "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A=="],
|
||||
|
||||
"@node-rs/argon2-darwin-arm64": ["@node-rs/argon2-darwin-arm64@1.7.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw=="],
|
||||
|
||||
"@node-rs/argon2-darwin-x64": ["@node-rs/argon2-darwin-x64@1.7.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw=="],
|
||||
|
||||
"@node-rs/argon2-freebsd-x64": ["@node-rs/argon2-freebsd-x64@1.7.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA=="],
|
||||
|
||||
"@node-rs/argon2-linux-arm-gnueabihf": ["@node-rs/argon2-linux-arm-gnueabihf@1.7.0", "", { "os": "linux", "cpu": "arm" }, "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg=="],
|
||||
|
||||
"@node-rs/argon2-linux-arm64-gnu": ["@node-rs/argon2-linux-arm64-gnu@1.7.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g=="],
|
||||
|
||||
"@node-rs/argon2-linux-arm64-musl": ["@node-rs/argon2-linux-arm64-musl@1.7.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A=="],
|
||||
|
||||
"@node-rs/argon2-linux-x64-gnu": ["@node-rs/argon2-linux-x64-gnu@1.7.0", "", { "os": "linux", "cpu": "x64" }, "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ=="],
|
||||
|
||||
"@node-rs/argon2-linux-x64-musl": ["@node-rs/argon2-linux-x64-musl@1.7.0", "", { "os": "linux", "cpu": "x64" }, "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA=="],
|
||||
|
||||
"@node-rs/argon2-wasm32-wasi": ["@node-rs/argon2-wasm32-wasi@1.7.0", "", { "dependencies": { "@emnapi/core": "^0.45.0", "@emnapi/runtime": "^0.45.0", "@tybys/wasm-util": "^0.8.1", "memfs-browser": "^3.4.13000" }, "cpu": "none" }, "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w=="],
|
||||
|
||||
"@node-rs/argon2-win32-arm64-msvc": ["@node-rs/argon2-win32-arm64-msvc@1.7.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA=="],
|
||||
|
||||
"@node-rs/argon2-win32-ia32-msvc": ["@node-rs/argon2-win32-ia32-msvc@1.7.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg=="],
|
||||
|
||||
"@node-rs/argon2-win32-x64-msvc": ["@node-rs/argon2-win32-x64-msvc@1.7.0", "", { "os": "win32", "cpu": "x64" }, "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q=="],
|
||||
|
||||
"@node-rs/bcrypt": ["@node-rs/bcrypt@1.9.0", "", { "optionalDependencies": { "@node-rs/bcrypt-android-arm-eabi": "1.9.0", "@node-rs/bcrypt-android-arm64": "1.9.0", "@node-rs/bcrypt-darwin-arm64": "1.9.0", "@node-rs/bcrypt-darwin-x64": "1.9.0", "@node-rs/bcrypt-freebsd-x64": "1.9.0", "@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0", "@node-rs/bcrypt-linux-arm64-gnu": "1.9.0", "@node-rs/bcrypt-linux-arm64-musl": "1.9.0", "@node-rs/bcrypt-linux-x64-gnu": "1.9.0", "@node-rs/bcrypt-linux-x64-musl": "1.9.0", "@node-rs/bcrypt-wasm32-wasi": "1.9.0", "@node-rs/bcrypt-win32-arm64-msvc": "1.9.0", "@node-rs/bcrypt-win32-ia32-msvc": "1.9.0", "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" } }, "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig=="],
|
||||
|
||||
"@node-rs/bcrypt-android-arm-eabi": ["@node-rs/bcrypt-android-arm-eabi@1.9.0", "", { "os": "android", "cpu": "arm" }, "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA=="],
|
||||
|
||||
"@node-rs/bcrypt-android-arm64": ["@node-rs/bcrypt-android-arm64@1.9.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A=="],
|
||||
|
||||
"@node-rs/bcrypt-darwin-arm64": ["@node-rs/bcrypt-darwin-arm64@1.9.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw=="],
|
||||
|
||||
"@node-rs/bcrypt-darwin-x64": ["@node-rs/bcrypt-darwin-x64@1.9.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug=="],
|
||||
|
||||
"@node-rs/bcrypt-freebsd-x64": ["@node-rs/bcrypt-freebsd-x64@1.9.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg=="],
|
||||
|
||||
"@node-rs/bcrypt-linux-arm-gnueabihf": ["@node-rs/bcrypt-linux-arm-gnueabihf@1.9.0", "", { "os": "linux", "cpu": "arm" }, "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA=="],
|
||||
|
||||
"@node-rs/bcrypt-linux-arm64-gnu": ["@node-rs/bcrypt-linux-arm64-gnu@1.9.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q=="],
|
||||
|
||||
"@node-rs/bcrypt-linux-arm64-musl": ["@node-rs/bcrypt-linux-arm64-musl@1.9.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew=="],
|
||||
|
||||
"@node-rs/bcrypt-linux-x64-gnu": ["@node-rs/bcrypt-linux-x64-gnu@1.9.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ=="],
|
||||
|
||||
"@node-rs/bcrypt-linux-x64-musl": ["@node-rs/bcrypt-linux-x64-musl@1.9.0", "", { "os": "linux", "cpu": "x64" }, "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg=="],
|
||||
|
||||
"@node-rs/bcrypt-wasm32-wasi": ["@node-rs/bcrypt-wasm32-wasi@1.9.0", "", { "dependencies": { "@emnapi/core": "^0.45.0", "@emnapi/runtime": "^0.45.0", "@tybys/wasm-util": "^0.8.1", "memfs-browser": "^3.4.13000" }, "cpu": "none" }, "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw=="],
|
||||
|
||||
"@node-rs/bcrypt-win32-arm64-msvc": ["@node-rs/bcrypt-win32-arm64-msvc@1.9.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw=="],
|
||||
|
||||
"@node-rs/bcrypt-win32-ia32-msvc": ["@node-rs/bcrypt-win32-ia32-msvc@1.9.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA=="],
|
||||
|
||||
"@node-rs/bcrypt-win32-x64-msvc": ["@node-rs/bcrypt-win32-x64-msvc@1.9.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w=="],
|
||||
|
||||
"@petamoriken/float16": ["@petamoriken/float16@3.9.1", "", {}, "sha512-j+ejhYwY6PeB+v1kn7lZFACUIG97u90WxMuGosILFsl9d4Ovi0sjk0GlPfoEcx+FzvXZDAfioD+NGnnPamXgMA=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.0.9", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "tailwindcss": "4.0.9" } }, "sha512-tOJvdI7XfJbARYhxX+0RArAhmuDcczTC46DGCEziqxzzbIaPnfYaIyRT31n4u8lROrsO7Q6u/K9bmQHL2uL1bQ=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.0.9", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.0.9", "@tailwindcss/oxide-darwin-arm64": "4.0.9", "@tailwindcss/oxide-darwin-x64": "4.0.9", "@tailwindcss/oxide-freebsd-x64": "4.0.9", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.9", "@tailwindcss/oxide-linux-arm64-gnu": "4.0.9", "@tailwindcss/oxide-linux-arm64-musl": "4.0.9", "@tailwindcss/oxide-linux-x64-gnu": "4.0.9", "@tailwindcss/oxide-linux-x64-musl": "4.0.9", "@tailwindcss/oxide-win32-arm64-msvc": "4.0.9", "@tailwindcss/oxide-win32-x64-msvc": "4.0.9" } }, "sha512-eLizHmXFqHswJONwfqi/WZjtmWZpIalpvMlNhTM99/bkHtUs6IqgI1XQ0/W5eO2HiRQcIlXUogI2ycvKhVLNcA=="],
|
||||
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.0.9", "", { "os": "android", "cpu": "arm64" }, "sha512-YBgy6+2flE/8dbtrdotVInhMVIxnHJPbAwa7U1gX4l2ThUIaPUp18LjB9wEH8wAGMBZUb//SzLtdXXNBHPUl6Q=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.0.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pWdl4J2dIHXALgy2jVkwKBmtEb73kqIfMpYmcgESr7oPQ+lbcQ4+tlPeVXaSAmang+vglAfFpXQCOvs/aGSqlw=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.0.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-4Dq3lKp0/C7vrRSkNPtBGVebEyWt9QPPlQctxJ0H3MDyiQYvzVYf8jKow7h5QkWNe8hbatEqljMj/Y0M+ERYJg=="],
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.0.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-k7U1RwRODta8x0uealtVt3RoWAWqA+D5FAOsvVGpYoI6ObgmnzqWW6pnVwz70tL8UZ/QXjeMyiICXyjzB6OGtQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.0.9", "", { "os": "linux", "cpu": "arm" }, "sha512-NDDjVweHz2zo4j+oS8y3KwKL5wGCZoXGA9ruJM982uVJLdsF8/1AeKvUwKRlMBpxHt1EdWJSAh8a0Mfhl28GlQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.0.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-jk90UZ0jzJl3Dy1BhuFfRZ2KP9wVKMXPjmCtY4U6fF2LvrjP5gWFJj5VHzfzHonJexjrGe1lMzgtjriuZkxagg=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.0.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-3eMjyTC6HBxh9nRgOHzrc96PYh1/jWOwHZ3Kk0JN0Kl25BJ80Lj9HEvvwVDNTgPg154LdICwuFLuhfgH9DULmg=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.0.9", "", { "os": "linux", "cpu": "x64" }, "sha512-v0D8WqI/c3WpWH1kq/HP0J899ATLdGZmENa2/emmNjubT0sWtEke9W9+wXeEoACuGAhF9i3PO5MeyditpDCiWQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.0.9", "", { "os": "linux", "cpu": "x64" }, "sha512-Kvp0TCkfeXyeehqLJr7otsc4hd/BUPfcIGrQiwsTVCfaMfjQZCG7DjI+9/QqPZha8YapLA9UoIcUILRYO7NE1Q=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.0.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-m3+60T/7YvWekajNq/eexjhV8z10rswcz4BC9bioJ7YaN+7K8W2AmLmG0B79H14m6UHE571qB0XsPus4n0QVgQ=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.0.9", "", { "os": "win32", "cpu": "x64" }, "sha512-dpc05mSlqkwVNOUjGu/ZXd5U1XNch1kHFJ4/cHkZFvaW1RzbHmRt24gvM8/HC6IirMxNarzVw4IXVtvrOoZtxA=="],
|
||||
|
||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.0.9", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.0.9", "@tailwindcss/oxide": "4.0.9", "lightningcss": "^1.29.1", "postcss": "^8.4.41", "tailwindcss": "4.0.9" } }, "sha512-BT/E+pdMqulavEAVM5NCpxmGEwHiLDPpkmg/c/X25ZBW+izTe+aZ+v1gf/HXTrihRoCxrUp5U4YyHsBTzspQKQ=="],
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.8.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.4", "", { "dependencies": { "bun-types": "1.2.4" } }, "sha512-QtuV5OMR8/rdKJs213iwXDpfVvnskPXY/S0ZiFbsTjQZycuqPbMW8Gf/XhLfwE5njW8sxI2WjISURXPlHypMFA=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.8", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
|
||||
|
||||
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
|
||||
|
||||
"brisa": ["brisa@0.2.7", "", { "dependencies": { "astring": "1.9.0", "csstype": "3.1.3", "diff-dom-streaming": "0.6.5", "meriyah": "6.0.5" }, "bin": { "brisa": "index.js" } }, "sha512-76fjLQkwfbAxdkA7bpQf7RUdvwsuC6l1nsNndn3yL6C2uD+SQ9RvHTuqwt8hWOz37j2zfN31AIxkJkuA9zwFMg=="],
|
||||
|
||||
"brisa-tailwindcss": ["brisa-tailwindcss@0.2.7", "", { "peerDependencies": { "@tailwindcss/postcss": "^4.0.0", "postcss": "^8.4.48", "tailwindcss": "^4.0.1" } }, "sha512-ZH4iSEWqY1V+G2sfn7l9YpR/d0AcEK+pWbpIQgoPeFG/qeyDVihHahg7Vp2ZWVp8Bbqq4v0vUhI09zV+OJLFdA=="],
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.4", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-nDPymR207ZZEoWD4AavvEaa/KZe/qlrbMSchqpQwovPZCKc7pwMoENjEtHgMKaAjJhy+x6vfqSBA1QU3bJgs0Q=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"daisyui": ["daisyui@5.0.0", "", {}, "sha512-U0K9Bac3Bi3zZGm6ojrw12F0vBHTpEgf46zv/BYxLe07hF0Xnx7emIQliwaRBgJuYhY0BhwQ6wSnq5cJXHA2yA=="],
|
||||
|
||||
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
||||
|
||||
"diff-dom-streaming": ["diff-dom-streaming@0.6.5", "", {}, "sha512-S6a7AVm5S0cwOkIYLTg+ilPaCnHM4/QoPol11PLYRKYqud/MfW+wW54+K2zBFqNY3DoNZZXO8yaF3iwFeJkz+A=="],
|
||||
|
||||
"drizzle-kit": ["drizzle-kit@0.30.5", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0", "gel": "^2.0.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-l6dMSE100u7sDaTbLczibrQZjA35jLsHNqIV+jmhNVO3O8jzM6kywMOmV9uOz9ZVSCMPQhAZEFjL/qDPVrqpUA=="],
|
||||
|
||||
"drizzle-orm": ["drizzle-orm@0.40.0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-7ptk/HQiMSrEZHnAsSlBESXWj52VwgMmyTEfoNmpNN2ZXpcz13LwHfXTIghsAEud7Z5UJhDOp8U07ujcqme7wg=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
|
||||
|
||||
"env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
|
||||
|
||||
"esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
|
||||
|
||||
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
|
||||
|
||||
"fs-monkey": ["fs-monkey@1.0.6", "", {}, "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg=="],
|
||||
|
||||
"gel": ["gel@2.0.0", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Oq3Fjay71s00xzDc0BF/mpcLmnA+uRqMEJK8p5K4PaZjUEsxaeo+kR9OHBVAf289/qPd+0OcLOLUN0UhqiUCog=="],
|
||||
|
||||
"get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
|
||||
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.29.1", "", { "dependencies": { "detect-libc": "^1.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.1", "lightningcss-darwin-x64": "1.29.1", "lightningcss-freebsd-x64": "1.29.1", "lightningcss-linux-arm-gnueabihf": "1.29.1", "lightningcss-linux-arm64-gnu": "1.29.1", "lightningcss-linux-arm64-musl": "1.29.1", "lightningcss-linux-x64-gnu": "1.29.1", "lightningcss-linux-x64-musl": "1.29.1", "lightningcss-win32-arm64-msvc": "1.29.1", "lightningcss-win32-x64-msvc": "1.29.1" } }, "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q=="],
|
||||
|
||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw=="],
|
||||
|
||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.29.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA=="],
|
||||
|
||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.29.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ=="],
|
||||
|
||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.29.1", "", { "os": "linux", "cpu": "arm" }, "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg=="],
|
||||
|
||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.29.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ=="],
|
||||
|
||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.29.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw=="],
|
||||
|
||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.29.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw=="],
|
||||
|
||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.29.1", "", { "os": "linux", "cpu": "x64" }, "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw=="],
|
||||
|
||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.29.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog=="],
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.1", "", { "os": "win32", "cpu": "x64" }, "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q=="],
|
||||
|
||||
"memfs": ["memfs@3.5.3", "", { "dependencies": { "fs-monkey": "^1.0.4" } }, "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw=="],
|
||||
|
||||
"memfs-browser": ["memfs-browser@3.5.10302", "", { "dependencies": { "memfs": "3.5.3" } }, "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw=="],
|
||||
|
||||
"meriyah": ["meriyah@6.0.5", "", {}, "sha512-SrMqQCox7TTwtftWKHy/ZaVe+ZRpRl20pAgDo+PS9hzcAJrMjYsBJQPPiLXTnjztrqdfGS+Zz99r6Bwvydta1w=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
|
||||
|
||||
"oslo": ["oslo@1.2.1", "", { "dependencies": { "@node-rs/argon2": "1.7.0", "@node-rs/bcrypt": "1.9.0" } }, "sha512-HfIhB5ruTdQv0XX2XlncWQiJ5SIHZ7NHZhVyHth0CSZ/xzge00etRyYy/3wp/Dsu+PkxMC+6+B2lS/GcKoewkA=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
||||
|
||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||
|
||||
"semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"shell-quote": ["shell-quote@1.8.2", "", {}, "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.0.9", "", {}, "sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw=="],
|
||||
|
||||
"tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="],
|
||||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
[test]
|
||||
preload = "brisa/test"
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "@star-kitten/eve-web",
|
||||
"module": "src/pages/index.tsx",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bunx dotenvx run -f .env.development -- brisa dev",
|
||||
"dev:debug": "brisa dev --debug",
|
||||
"build": "brisa build",
|
||||
"start": "bunx dotenvx run -f .env.production -- brisa start"
|
||||
},
|
||||
"dependencies": {
|
||||
"brisa": "0.2.7",
|
||||
"brisa-tailwindcss": "0.2.7",
|
||||
"drizzle-orm": "^0.40.0",
|
||||
"oslo": "^1.2.1",
|
||||
"@star-kitten/util": "workspace:^0.0.0",
|
||||
"@star-kitten/eve": "workspace:^0.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"daisyui": "^5.0.0",
|
||||
"drizzle-kit": "^0.30.5",
|
||||
"typescript": "latest"
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import type { RequestContext } from 'brisa';
|
||||
import type { EVEAuth } from '@/middleware';
|
||||
import { getCookies, removeCookie, setCookie } from '@/utils';
|
||||
import { CharacterAPI, characterIdFromToken } from '@star-kitten/eve/esi';
|
||||
import { CharacterHelper, UserHelper } from '@star-kitten/eve/db';
|
||||
|
||||
// GET /api/auth/callback
|
||||
export async function GET(request: RequestContext) {
|
||||
const eveauth: EVEAuth = request.store.get('eveauth');
|
||||
const response = new Response('', { status: 302 });
|
||||
|
||||
try {
|
||||
const cookies = getCookies(request.headers);
|
||||
const cookieDiscordID = cookies['discordID'];
|
||||
if (!cookieDiscordID) {
|
||||
throw new Error(`Missing discordID cookie in /api/auth/callback`);
|
||||
}
|
||||
|
||||
const cookieCharacterID = cookies['characterID'];
|
||||
const token = await eveauth.validate(response);
|
||||
const characterID = characterIdFromToken(token.access_token);
|
||||
if (cookieCharacterID && parseInt(cookieCharacterID) !== characterID) {
|
||||
throw new Error(`Character ID mismatch: ${cookieCharacterID} !== ${characterID}`);
|
||||
}
|
||||
|
||||
let user = UserHelper.findByDiscordId(cookieDiscordID);
|
||||
let character = CharacterHelper.findByUserAndEveID(user.id, Number(characterID));
|
||||
|
||||
if (!user) {
|
||||
user = UserHelper.create(cookieDiscordID);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new Error(`Something went wrong with creating a user for id: ${cookieDiscordID}`);
|
||||
}
|
||||
|
||||
if (!character) {
|
||||
const data = await CharacterAPI.getCharacterPublicData(characterID);
|
||||
if (!data) {
|
||||
throw new Error(`Failed to retreive character public data for id: ${characterID} - unable to create character`);
|
||||
}
|
||||
character = CharacterHelper.create(characterID, data.name || 'UNKNOWN NAME', user, token);
|
||||
|
||||
// refetch from db to get id
|
||||
user = UserHelper.findByDiscordId(cookieDiscordID);
|
||||
character = CharacterHelper.findByUserAndEveID(user.id, Number(characterID));
|
||||
if (!character) {
|
||||
throw new Error(`Failed to retreive character from db for id: ${characterID}`);
|
||||
}
|
||||
|
||||
if (!user.mainCharacterID) {
|
||||
user.mainCharacterID = character.id;
|
||||
UserHelper.save(user);
|
||||
}
|
||||
} else {
|
||||
// Update existing character with new token
|
||||
character.accessToken = token.access_token;
|
||||
character.expiresAt = new Date(Date.now() + token.expires_in * 1000);
|
||||
character.refreshToken = token.refresh_token;
|
||||
CharacterHelper.save(character);
|
||||
}
|
||||
|
||||
setCookie(response, 'currentUser', user.id + '', 60 * 60 * 24 * 30 /* 30 days */);
|
||||
response.headers.set('location', '/auth/success');
|
||||
} catch (err) {
|
||||
console.error(`Error: Callback failed with ${err}`);
|
||||
response.headers.set('location', '/auth/error');
|
||||
return response;
|
||||
} finally {
|
||||
removeCookie(response, 'discordID');
|
||||
removeCookie(response, 'characterID');
|
||||
removeCookie(response, 'state');
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { EVEAuth } from '@/middleware';
|
||||
import { setCookie } from '@/utils';
|
||||
import type { RequestContext } from 'brisa';
|
||||
import { CharacterHelper, UserHelper } from '@star-kitten/eve/db';
|
||||
|
||||
// GET /api/auth/discordID/:discordID/addScopes/characterID/:characterID/scopes/:scopes
|
||||
export async function GET({ store, route: { params } }: RequestContext) {
|
||||
const eveauth: EVEAuth = store.get('eveauth');
|
||||
|
||||
const discordID = params!['discordID'] as string;
|
||||
const characterID = params!['characterID'] as string;
|
||||
const requiredScopes = (params!['scopes'] as string).split(',');
|
||||
|
||||
const user = UserHelper.findByDiscordId(discordID);
|
||||
const character = CharacterHelper.findByUserAndEveID(user.id, Number(characterID));
|
||||
if (!character) {
|
||||
throw new Error(`Character ${characterID} not found`);
|
||||
}
|
||||
|
||||
const currentScopes = CharacterHelper.getScopes(character);
|
||||
const set = new Set(currentScopes);
|
||||
requiredScopes.forEach((scope) => set.add(scope));
|
||||
const scopes = Array.from(set).join(' ');
|
||||
|
||||
// As this is adding scopes, we need to redirect the user to the auth page
|
||||
const response = await eveauth.redirect(scopes);
|
||||
setCookie(response, 'discordID', discordID, 60 * 10 /* 10 min */);
|
||||
setCookie(response, 'characterID', characterID, 60 * 10 /* 10 min */);
|
||||
return response;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { EVEAuth } from '@/middleware';
|
||||
import { setCookie } from '@/utils';
|
||||
import type { RequestContext } from 'brisa';
|
||||
import { joinScopes, SCOPES } from '@star-kitten/eve/esi';
|
||||
|
||||
const allScopes = Object.values(SCOPES).filter((value) => typeof value === 'string') as string[];
|
||||
const allScopesString = allScopes.join(' ');
|
||||
|
||||
const module_Scopes: { [key: string]: string } = {
|
||||
'Full ESI': allScopesString,
|
||||
'Public': joinScopes(SCOPES.PUBLIC_DATA),
|
||||
'Characters': joinScopes(SCOPES.PUBLIC_DATA),
|
||||
'Mail': joinScopes(SCOPES.MAIL_READ_MAIL, SCOPES.MAIL_SEND_MAIL, SCOPES.MAIL_ORGANIZE_MAIL),
|
||||
'Skills': joinScopes(SCOPES.SKILLS_READ_SKILLQUEUE, SCOPES.SKILLS_READ_SKILLS),
|
||||
}
|
||||
|
||||
// GET /api/auth/discordID/:discordID/characterID/:characterID/modules/:modules
|
||||
export async function GET({ store, route: { params } }: RequestContext) {
|
||||
// we need to check the modules that were sent, in order to get the correct scopes
|
||||
// that each module requires.
|
||||
let modules = (params!['modules'] as string).split(' ');
|
||||
let scopes = joinScopes(...modules.map((module) => module_Scopes[module]));
|
||||
|
||||
const eveauth: EVEAuth = store.get('eveauth');
|
||||
const response = await eveauth.redirect(scopes);
|
||||
setCookie(response, 'discordID', params!['discordID'] as string, 60 * 10 /* 10 min */);
|
||||
setCookie(response, 'characterID', params!['characterID'] as string, 60 * 10 /* 10 min */);
|
||||
return response;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { EVEAuth } from '@/middleware';
|
||||
import { setCookie } from '@/utils';
|
||||
import type { RequestContext } from 'brisa';
|
||||
import { CharacterHelper, UserHelper } from '@star-kitten/eve/db';
|
||||
import { joinScopes } from '@star-kitten/eve/esi';
|
||||
|
||||
// GET /api/auth/discordID/:discordID/characterID/:characterID/refresh
|
||||
export async function GET({ store, route: { params } }: RequestContext) {
|
||||
|
||||
const discordID = params!['discordID'] as string;
|
||||
const characterID = params!['characterID'] as string;
|
||||
|
||||
const user = UserHelper.findByDiscordId(discordID);
|
||||
const character = CharacterHelper.findByUserAndEveID(user.id, Number(characterID));
|
||||
if (!user) {
|
||||
throw new Error(`User not found for discordID: ${discordID}`);
|
||||
}
|
||||
if (!character) {
|
||||
throw new Error(`Character not found for user: ${user.id} and characterID: ${characterID}`);
|
||||
}
|
||||
const scopes = CharacterHelper.getScopes(character);
|
||||
|
||||
const eveauth: EVEAuth = store.get('eveauth');
|
||||
const response = await eveauth.redirect(joinScopes(...scopes));
|
||||
setCookie(response, 'discordID', discordID, 60 * 10 /* 10 min */);
|
||||
setCookie(response, 'characterID', characterID, 60 * 10 /* 10 min */);
|
||||
return response;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import type { EVEAuth } from '@/middleware';
|
||||
import { setCookie } from '@/utils';
|
||||
import type { RequestContext } from 'brisa';
|
||||
|
||||
// GET /api/auth/discordID/:discordID/characterID/:characterID/scopes/:scopes
|
||||
export async function GET({ store, route: { params } }: RequestContext) {
|
||||
// this is used to set the scopes that were sent, so just pass them along to auth directly
|
||||
// with the provided scopes
|
||||
const eveauth: EVEAuth = store.get('eveauth');
|
||||
const response = await eveauth.redirect(params!['scopes'] as string);
|
||||
setCookie(response, 'discordID', params!['discordID'] as string, 60 * 10 /* 10 min */);
|
||||
setCookie(response, 'characterID', params!['characterID'] as string, 60 * 10 /* 10 min */);
|
||||
return response;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { EVEAuth } from '@/middleware';
|
||||
import { setCookie } from '@/utils';
|
||||
import type { RequestContext } from 'brisa';
|
||||
|
||||
// GET /api/auth/discordID/:discordID
|
||||
export async function GET({ store, route: { params }}: RequestContext) {
|
||||
// called when adding a new character, so just redirect to auth and set cookies
|
||||
const eveauth: EVEAuth = store.get('eveauth');
|
||||
const response = await eveauth.redirect('publicData');
|
||||
setCookie(response, 'discordID', params!['discordID'] as string, 60 * 10 /* 10 min */);
|
||||
return response;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { setCookie } from '@/utils';
|
||||
import type { RequestContext } from 'brisa';
|
||||
import { CharacterHelper, UserHelper } from '@star-kitten/eve/db'
|
||||
|
||||
// GET /api/auth/discordID/:discordID/removeScopes/characterID/:characterID/scopes/:scopes
|
||||
export function GET({ route: { params } }: RequestContext) {
|
||||
const discordID = params!['discordID'] as string;
|
||||
const characterID = params!['characterID'] as string;
|
||||
const removeScopes = (params!['scopes'] as string).split(',');
|
||||
|
||||
const user = UserHelper.findByDiscordId(discordID);
|
||||
const character = CharacterHelper.findByUserAndEveID(user.id, Number(characterID));
|
||||
if (!character) {
|
||||
throw new Error(`Character ${characterID} not found`);
|
||||
}
|
||||
const currentScopes = CharacterHelper.getScopes(character);
|
||||
const set = new Set(currentScopes);
|
||||
removeScopes.forEach((scope) => set.delete(scope));
|
||||
|
||||
// As this is removing scopes, we can do this without user interaction
|
||||
CharacterHelper.refreshTokens(character, Array.from(set).join(' '));
|
||||
|
||||
// redirect to success page
|
||||
const response = new Response('', { status: 302 });
|
||||
response.headers.set('location', '/auth/success');
|
||||
setCookie(response, 'discordID', params!['discordID'] as string, 60 * 10 /* 10 min */);
|
||||
setCookie(response, 'discordID', characterID, 60 * 10 /* 10 min */);
|
||||
return response;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { RequestContext } from 'brisa';
|
||||
import type { EVEAuth } from '@/middleware';
|
||||
|
||||
// GET /api/auth/
|
||||
export function GET(request: RequestContext) {
|
||||
const eveauth: EVEAuth = request.store.get('eveauth');
|
||||
return eveauth.redirect();
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { renderComponent } from 'brisa/server';
|
||||
|
||||
export default function CounterServer({
|
||||
initialValue = 0,
|
||||
}: {
|
||||
initialValue: number;
|
||||
}) {
|
||||
function increment(e: Event) {
|
||||
const value = Number((e.target as HTMLButtonElement).dataset.value);
|
||||
renderComponent({ element: <CounterServer initialValue={value + 1} /> });
|
||||
}
|
||||
|
||||
function decrement(e: Event) {
|
||||
const value = Number((e.target as HTMLButtonElement).dataset.value);
|
||||
renderComponent({ element: <CounterServer initialValue={value - 1} /> });
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="counter">
|
||||
<div class="counter-container">
|
||||
<h2>Server counter</h2>
|
||||
<button
|
||||
data-value={initialValue}
|
||||
class="increment-button"
|
||||
onClick={increment}
|
||||
></button>
|
||||
<div class="counter-value">{initialValue}</div>
|
||||
<button
|
||||
data-value={initialValue}
|
||||
class="decrement-button"
|
||||
onClick={decrement}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer class="footer sm:footer-horizontal bg-neutral text-neutral-content items-center p-4">
|
||||
<aside class="grid-flow-col items-center">
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
class="fill-current">
|
||||
<path
|
||||
d="M22.672 15.226l-2.432.811.841 2.515c.33 1.019-.209 2.127-1.23 2.456-1.15.325-2.148-.321-2.463-1.226l-.84-2.518-5.013 1.677.84 2.517c.391 1.203-.434 2.542-1.831 2.542-.88 0-1.601-.564-1.86-1.314l-.842-2.516-2.431.809c-1.135.328-2.145-.317-2.463-1.229-.329-1.018.211-2.127 1.231-2.456l2.432-.809-1.621-4.823-2.432.808c-1.355.384-2.558-.59-2.558-1.839 0-.817.509-1.582 1.327-1.846l2.433-.809-.842-2.515c-.33-1.02.211-2.129 1.232-2.458 1.02-.329 2.13.209 2.461 1.229l.842 2.515 5.011-1.677-.839-2.517c-.403-1.238.484-2.553 1.843-2.553.819 0 1.585.509 1.85 1.326l.841 2.517 2.431-.81c1.02-.33 2.131.211 2.461 1.229.332 1.018-.21 2.126-1.23 2.456l-2.433.809 1.622 4.823 2.433-.809c1.242-.401 2.557.484 2.557 1.838 0 .819-.51 1.583-1.328 1.847m-8.992-6.428l-5.01 1.675 1.619 4.828 5.011-1.674-1.62-4.829z"></path>
|
||||
</svg>
|
||||
<p>Copyright © {new Date().getFullYear()} Star Kitten Cafe - All rights reserved</p>
|
||||
</aside>
|
||||
<nav class="grid-flow-col gap-4 md:place-self-center md:justify-self-end">
|
||||
<a href="https://github.com/roman-kaas/star-kitten" target='_blank'>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 98 96"
|
||||
class="fill-current">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/>
|
||||
</svg>
|
||||
</a>
|
||||
{/* <a>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="fill-current">
|
||||
<path
|
||||
d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="fill-current">
|
||||
<path
|
||||
d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"></path>
|
||||
</svg>
|
||||
</a> */}
|
||||
</nav>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { CharacterHelper, type User } from '@star-kitten/eve/db';
|
||||
import { CharacterAPI } from '@star-kitten/eve/esi';
|
||||
|
||||
export default function Nav({
|
||||
user,
|
||||
}: {
|
||||
user: User;
|
||||
}) {
|
||||
const main = user.mainCharacterID && CharacterHelper.find(user.mainCharacterID);
|
||||
return (
|
||||
<div class="navbar bg-base-100 shadow-sm">
|
||||
<div class="flex-1">
|
||||
<a class="btn btn-ghost text-xl">Star Kitten</a>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" placeholder="Search" class="input input-bordered w-24 md:w-auto" />
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabIndex={0} role="button" class="btn btn-ghost btn-circle avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img
|
||||
alt="Tailwind CSS Navbar component"
|
||||
src={main && CharacterAPI.getPortraitURL(main.eveID) || ''} />
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
|
||||
<li>
|
||||
{main && main.name}
|
||||
</li>
|
||||
<li>
|
||||
<a class="justify-between">
|
||||
Characters
|
||||
<span class="badge">{user.characterIDs.length}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li><a>Link Character</a></li>
|
||||
<li><a>Settings</a></li>
|
||||
<li><a>Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { type Character } from '@star-kitten/eve/db';
|
||||
import { calculateTrainingPercentage, getCharacterSkillQueue } from '@star-kitten/eve/esi';
|
||||
import { getSkill, getType } from '@star-kitten/eve/models';
|
||||
|
||||
export default async function SkillQueueStat({
|
||||
character,
|
||||
}: {
|
||||
character: Character;
|
||||
}) {
|
||||
|
||||
const queue = await getCharacterSkillQueue(character);
|
||||
const current = queue?.find((skill) => skill.queue_position === 0);
|
||||
if (!current || !current.start_date) {
|
||||
return (
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-secondary">
|
||||
</div>
|
||||
<div class="stat-title">No Skills Training</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
const skill = await getSkill(current.skill_id);
|
||||
const percentage = calculateTrainingPercentage(current) * 100;
|
||||
return (
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-secondary">
|
||||
</div>
|
||||
<div class="stat-title">Currently Training</div>
|
||||
<div class="stat-value">{(await getType(skill.type_id)).name.en} {current.finished_level}</div>
|
||||
<div class="stat-desc"><progress class="progress progress-primary w-full" value={percentage} max="100"></progress></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SkillQueueStat.suspense = () => (
|
||||
<div class="stat">
|
||||
<div class="stat-figure text-secondary">
|
||||
</div>
|
||||
<div class="stat-title">Currently Training</div>
|
||||
<div class="flex w-52 flex-col gap-4">
|
||||
<div class="skeleton h-16 w-full"></div>
|
||||
<div class="skeleton h-4 w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user