Skip to content

Commit

Permalink
fixed recursion issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Cameron Campbell authored and Cameron Campbell committed Aug 6, 2024
1 parent ab3e578 commit cc7d7bc
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

</div>

A strictly typed Roblox API wrapper which supports both classic (BEDEV & BEDEV2) and OpenCloud endpoints.
A strictly typed Roblox API wrapper which supports both classic and OpenCloud endpoints.

- - -

Expand Down
8 changes: 1 addition & 7 deletions build.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as esbuild from 'esbuild';
import esbuildPluginTsc from "esbuild-plugin-tsc"

const settings = {
entryPoints: ["src/**/*"],
Expand All @@ -8,12 +7,7 @@ const settings = {
//keepNames: true,
format: 'cjs',
platform: 'node',
target: ['node16'],
plugins: [
esbuildPluginTsc({
force: true
}),
],
target: ['node18', 'node16', 'node20', 'node22']
} satisfies esbuild.BuildOptions

await esbuild.build(settings);
Binary file modified bun.lockb
Binary file not shown.
Binary file removed graph.png
Binary file not shown.
45 changes: 30 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "openblox",
"description": "Roblox API Wrapper For Both Classic And OpenCloud APIs.",
"type": "commonjs",
"version": "1.0.28",
"version": "1.0.29",
"license": "MIT",
"bugs": {
"url": "https://github.com/MightyPart/openblox/issues"
Expand All @@ -22,14 +22,21 @@
"exports_DEV": {
".": "./src/index.ts",
"./config": "./src/config/index.ts",

"./classic": "./src/apis/classic/index.ts",
"./classic/*": "./src/apis/classic/*/index.ts",

"./cloud": "./src/apis/cloud/index.ts",
"./cloud/*": "./src/apis/cloud/*/index.ts",

"./cache/adapters": "./src/cache/cacheAdapters/index.ts",
"./http": "./src/http/index.ts",
"./http": "./src/http/http.utils.ts",
"./types": "./src/types.ts",
"./queries/cloud": "./src/queries/cloud/index.ts"

"./queries/cloud": "./src/queries/cloud/index.ts",
"./queries/classic": "./src/queries/classic/index.ts",

"./helpers": "./src/helpers/index.ts"
},
"exports": {
".": {
Expand All @@ -44,6 +51,7 @@
"types": "./dist/apis/classic/index.d.ts",
"default": "./dist/apis/classic/index.js"
},

"./classic/*": {
"types": "./dist/apis/classic/*/index.d.ts",
"default": "./dist/apis/classic/*/index.js"
Expand All @@ -52,10 +60,12 @@
"types": "./dist/apis/cloud/index.d.ts",
"default": "./dist/apis/cloud/index.js"
},

"./cloud/*": {
"types": "./dist/apis/cloud/*/index.d.ts",
"default": "./dist/apis/cloud/*/index.js"
},

"./cache/adapters": {
"types": "./dist/cache/cacheAdapters/index.d.ts",
"default": "./dist/cache/cacheAdapters/index.js"
Expand All @@ -68,37 +78,42 @@
"types": "./dist/types.d.ts",
"default": "./dist/types.ts"
},

"./queries/cloud": {
"types": "./dist/queries/cloud/index.d.ts",
"default": "./dist/queries/cloud/index.js"
},
"./queries/classic": {
"types": "./dist/queries/classic/index.d.ts",
"default": "./dist/queries/classic/index.js"
},

"./helpers": {
"types": "./dist/helpers/index.d.ts",
"default": "./dist/helpers/index.js"
}
},
"dependencies": {
"delete-cli": "^0.1.3",
"lodash": "^4.17.21",
"parse-roblox-errors": "^1.1.10",
"typeforge": "^0.0.21"
"parse-roblox-errors": "^1.1.10"
},
"devDependencies": {
"@types/lodash": "^4.17.0",
"@types/node": "^20.12.12",
"@types/node": "^22.1.0",
"esbuild": "^0.21.5",
"esbuild-plugin-tsc": "^0.4.0",
"prettier": "^3.2.5",
"tablemark": "^3.1.0",
"ts-arithmetic": "^0.1.1",
"ts-morph": "^22.0.0"
"ts-morph": "^22.0.0",
"typeforge": "^0.0.21",
"delete-cli": "^0.1.3"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"scripts": {
"build:code": "delete dist && tsc",
"build:code": "delete dist && bun run ./build.ts && tsc --emitDeclarationOnly",
"build:docs": "bun run ./docs/buildDocs.ts",
"build": "bun run build:docs && bun run build:code",
"graph:src": "madge ./src --image ./graph.png",
"graph:src:circular": "madge --circular ./src --image ./graph.png",
"graph:dist": "madge ./dist --image ./graph.png",
"graph:dist:circular": "madge --circular ./dist --image ./graph.png"
"build": "bun run build:docs && bun run build:code"
}
}
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
export * from "./apis/apiGroup"
2 changes: 2 additions & 0 deletions src/queries/cloud/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Users } from "./usersQuery"
//export { Group } from "./groupQuery"
158 changes: 158 additions & 0 deletions src/queries/cloud/usersQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// [ Modules ] ///////////////////////////////////////////////////////////////////
import { UsersApi } from "../../apis/cloud"
//////////////////////////////////////////////////////////////////////////////////


// [ Types ] /////////////////////////////////////////////////////////////////////
import { ArrayNonEmpty, ArrayToUnion, Identifier, ObjectKeepKeys, ObjectPrettify, ObjectPrettifyDeep, UnionPrettify, UnionToArray } from "typeforge"
import { PrettifiedUserInfoData, UserThumbnailSize } from "../../apis/cloud/users/users.types"
import { ArrayNonEmptyIfConst, SecureUrl } from "../../utils/utils.types"


type UnionKeepTypes<U, ToKeep> = U extends ToKeep ? U : never
type UnionRemoveTypes<U, ToKeep> = U extends ToKeep ? never : U


type UserInfoField = ArrayToUnion<typeof userInfoFields>
type UserThumbnailField = "thumbnail" | `thumbnail/${UserThumbnailSize}` | `thumbnail/${UserThumbnailSize}/${"PNG" | "JPEG"}` | `thumbnail/${UserThumbnailSize}/${"PNG" | "JPEG"}/${"ROUND" | "SQUARE"}`
type UsersField = UnionPrettify<UserInfoField | UserThumbnailField>


type CleanObject<
Obj extends Record<any, any>, Field extends string,
_ExludedIrrelevantKeys = ObjectPrettify<Partial<ObjectKeepKeys<Obj, Field>>>,
// @ts-ignore | `UnionToArray<Field>` is an array. `_ExludedIrrelevantKeys[Field]` is typesafe.
_MaybeNoObject = UnionToArray<Field>["length"] extends 1 ? _ExludedIrrelevantKeys[Field] : _ExludedIrrelevantKeys
> = _MaybeNoObject
//////////////////////////////////////////////////////////////////////////////////


// [ Variables ] /////////////////////////////////////////////////////////////////
const UsersApi_userInfo = UsersApi.userInfo
const UsersApi_userThumbnail = UsersApi.userThumbnail

const userInfoFields = [ "createTime", "id", "name", "displayName", "about", "locale", "premium", "idVerified", "socialNetworkProfiles" ] as const
//////////////////////////////////////////////////////////////////////////////////


// [ Private Functions ] /////////////////////////////////////////////////////////
const shellFn = (...args: any) => null

function uniq_fast(a: any[]) {
var seen: any = {};
var out = [];
var len = a.length;
var j = 0;
for(var i = 0; i < len; i++) {
var item = a[i];
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}


// Users Info --------------------------------------------------------------------
const getUserInfoSingle_forId = async (userId: Identifier, data: any, fields: ArrayNonEmpty<UserInfoField>) => {
const { data:userInfo } = await UsersApi_userInfo({ userId })
data[userId] = userInfo[fields[0]]
}

const getUserInfoMulti_forId = async (userId: Identifier, data: any, fields: ArrayNonEmpty<UserInfoField>) => {
const data_userId = data[userId]
const { data:userInfo } = await UsersApi_userInfo({ userId });
(fields as ArrayNonEmpty<UserInfoField>).forEach(field => data_userId[field] = userInfo[field])
}

const getUsersInfo_forIds = (
fields: ArrayNonEmpty<UserInfoField>, getUserInfo_forId: typeof getUserInfoSingle_forId | typeof getUserInfoMulti_forId
) => (
async (userIds: ArrayNonEmptyIfConst<Identifier>, data: any) => {
await Promise.all(userIds.map(userId => getUserInfo_forId(userId, data, fields)))
}
)
// -------------------------------------------------------------------------------


// User Thumbnails ---------------------------------------------------------------
const getUserThumbnailSingle_forId = async (
userId: Identifier, data: any, data_userId: any, thumbnail: string,
size?: UserThumbnailSize, format?: "PNG" | "JPEG", shape?: "ROUND" | "SQUARE"
) => {
const { data:userThumbnail } = await UsersApi_userThumbnail({ userId, size, format, shape })
if (!userThumbnail.done) return
data[userId] = userThumbnail.response.imageUri
}

const getUserThumbnailMulti_forId = async (
userId: Identifier, data: any, data_userId: any, thumbnail: string,
size?: UserThumbnailSize, format?: "PNG" | "JPEG", shape?: "ROUND" | "SQUARE"
) => {
const { data:userThumbnail } = await UsersApi_userThumbnail({ userId, size, format, shape })
if (!userThumbnail.done) return
data_userId[thumbnail] = userThumbnail.response.imageUri
}

const getUsersThumbnail_forIds = async (
userIds: ArrayNonEmptyIfConst<Identifier>, data: any, getUserThumbnail_forId: typeof getUserThumbnailMulti_forId | typeof getUserThumbnailSingle_forId,
thumnail: string, size?: UserThumbnailSize, format?: "PNG" | "JPEG", shape?: "ROUND" | "SQUARE"
) => {
return await Promise.all(userIds.map(userId => getUserThumbnail_forId(userId, data, data[userId], thumnail, size, format, shape)))
}

const getUsersThumbnails_forIds = (
fields: string[][], getUserThumbnail_forId: typeof getUserThumbnailMulti_forId | typeof getUserThumbnailSingle_forId
) => (
async (userIds: ArrayNonEmptyIfConst<Identifier>, data: any) => {
await Promise.all(fields.map(field => getUsersThumbnail_forIds(
userIds, data, getUserThumbnail_forId, field?.[0] as any as string,
field?.[2] as any as UserThumbnailSize, field?.[3] as any as "PNG" | "JPEG", field?.[4] as any as "ROUND" | "SQUARE",
)))
}
)
// -------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////


export const Users = {
get: <Field extends UsersField>(fields: ArrayNonEmptyIfConst<Field>) => {
fields = uniq_fast(fields) as ArrayNonEmptyIfConst<Field>
const isSingleField = (fields as Field[]).length == 1

const usersInfoFields =
(fields as Field[]).filter(field => userInfoFields.includes(field as UserInfoField)) as any as ArrayNonEmpty<UserInfoField>
const thisGetUsersInfo_forIds = usersInfoFields.length
? getUsersInfo_forIds(usersInfoFields, isSingleField ? getUserInfoSingle_forId : getUserInfoMulti_forId) : shellFn

const userThumbnailFields = (fields as Field[]).filter(field => field.startsWith("thumbnail")).map(field => [ field, ...field.split("/") ])
const thisGetUsersThumbnails_forIds = userThumbnailFields.length
? getUsersThumbnails_forIds(userThumbnailFields, isSingleField ? getUserThumbnailSingle_forId : getUserThumbnailMulti_forId) : shellFn

const createData = isSingleField
? (userIds: ArrayNonEmptyIfConst<Identifier>) => ({})
: (userIds: ArrayNonEmptyIfConst<Identifier>) => { const data: any = {}; userIds.forEach(userId => data[userId] = {}); return data }

return {
forIds: async <UserId extends Identifier>(userIds: ArrayNonEmptyIfConst<UserId>): Promise<{
data: ObjectPrettifyDeep<{
[Id in UserId]: CleanObject<
PrettifiedUserInfoData<Id> &
{ [Key in UnionKeepTypes<Field, `thumbnail${string}`>]: SecureUrl },
Field
>
}>
}> => {
const data: any = createData(userIds)

await Promise.all([
thisGetUsersInfo_forIds(userIds, data),
thisGetUsersThumbnails_forIds(userIds, data)
])

return { data }
}
}
}
}

0 comments on commit cc7d7bc

Please sign in to comment.