diff --git a/.changeset/khaki-lies-see.md b/.changeset/khaki-lies-see.md new file mode 100644 index 0000000..849536b --- /dev/null +++ b/.changeset/khaki-lies-see.md @@ -0,0 +1,5 @@ +--- +"@webiny/stdlib": patch +--- + +Add `mdbid` utility that generates MongoDB-compatible ObjectId hex strings via `bson-objectid`. Import from `@webiny/stdlib` — no DI setup required. diff --git a/README.md b/README.md index e1ecc29..2db6ecc 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ The package is ESM-only and ships three subpath exports. Because each is a separ | `immutableGet` / `immutableSet` / `immutableDelete` / `mutableSet` / `mutableDelete` | Dot-notation get/set/delete on nested objects — [docs](src/common/utils/dotProp/README.md) | | `toBoolean` / `isTruthy` / `isFalsy` | Semantic boolean coercion — [docs](src/common/utils/boolean/README.md) | | `uuid` | RFC 4122 v4 UUID generator (native + fallback) — [docs](src/common/utils/uuid/README.md) | +| `mdbid` | MongoDB-compatible ObjectId generator — [docs](src/common/utils/mdbid/README.md) | --- diff --git a/__tests__/mdbid.test.ts b/__tests__/mdbid.test.ts new file mode 100644 index 0000000..2026e6a --- /dev/null +++ b/__tests__/mdbid.test.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from "vitest"; +import { mdbid } from "../src/common/utils/mdbid/mdbid.js"; + +const OBJECTID_REGEX = /^[0-9a-f]{24}$/; + +describe("mdbid", () => { + it("returns a 24-character lowercase hex string", () => { + const id = mdbid(); + expect(id).toMatch(OBJECTID_REGEX); + }); + + it("generates unique values on successive calls", () => { + const ids = new Set(Array.from({ length: 1000 }, () => mdbid())); + expect(ids.size).toBe(1000); + }); + + it("returns a string, not an ObjectID instance", () => { + const id = mdbid(); + expect(typeof id).toBe("string"); + }); + + it("has exactly 24 characters", () => { + const id = mdbid(); + expect(id.length).toBe(24); + }); + + it("contains only lowercase hex characters", () => { + const id = mdbid(); + expect(id).toBe(id.toLowerCase()); + }); +}); diff --git a/package.json b/package.json index c7e8dcb..e10f0c6 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ ], "dependencies": { "@webiny/di": "^1.0.1", + "bson-objectid": "^2.0.4", "dot-prop": "^10.1.0", "fast-glob": "^3.3.3", "pino": "^10.3.1", @@ -34,8 +35,8 @@ }, "devDependencies": { "@changesets/cli": "^2.31.0", - "@types/node": ">=24", - "@typescript/native-preview": "^7.0.0-dev.20260603.1", + "@types/node": "^25.9.1", + "@typescript/native-preview": "^7.0.0-dev.20260605.1", "@vitest/coverage-v8": "^4.1.8", "adio": "^3.0.1", "happy-dom": "^20.10.1", diff --git a/src/common/index.ts b/src/common/index.ts index d234917..d8c897a 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -21,3 +21,4 @@ export { immutableSet } from "./utils/dotProp/index.js"; export { uuid } from "./utils/uuid/index.js"; +export { mdbid } from "./utils/mdbid/index.js"; diff --git a/src/common/utils/README.md b/src/common/utils/README.md index ef817d0..14482de 100644 --- a/src/common/utils/README.md +++ b/src/common/utils/README.md @@ -7,3 +7,4 @@ Standalone utility functions exported from `@webiny/stdlib`. No DI container req | [boolean](boolean/README.md) | Semantic boolean coercion (`toBoolean`, `isTruthy`, `isFalsy`) | | [dotProp](dotProp/README.md) | Immutable and mutable get/set/delete on nested objects via dot-notation | | [uuid](uuid/README.md) | RFC 4122 v4 UUID generator (native `randomUUID` + `getRandomValues` fallback) | +| [mdbid](mdbid/README.md) | MongoDB-compatible ObjectId generator via `bson-objectid` | diff --git a/src/common/utils/mdbid/README.md b/src/common/utils/mdbid/README.md new file mode 100644 index 0000000..1c42df1 --- /dev/null +++ b/src/common/utils/mdbid/README.md @@ -0,0 +1,19 @@ +# mdbid + +Generates MongoDB-compatible ObjectId strings. Uses `bson-objectid` to produce 24-character, time-sortable, globally unique hex identifiers that match MongoDB's native ObjectId format. + +## API + +```ts +function mdbid(): string; +``` + +Returns a 24-character lowercase hex string (e.g. `"507f1f77bcf86cd799439011"`). + +## Usage + +```ts +import { mdbid } from "@webiny/stdlib"; + +const id = mdbid(); // "507f1f77bcf86cd799439011" +``` diff --git a/src/common/utils/mdbid/index.ts b/src/common/utils/mdbid/index.ts new file mode 100644 index 0000000..dd4523e --- /dev/null +++ b/src/common/utils/mdbid/index.ts @@ -0,0 +1 @@ +export { mdbid } from "./mdbid.js"; diff --git a/src/common/utils/mdbid/mdbid.ts b/src/common/utils/mdbid/mdbid.ts new file mode 100644 index 0000000..ff08f38 --- /dev/null +++ b/src/common/utils/mdbid/mdbid.ts @@ -0,0 +1,17 @@ +import _ObjectID from "bson-objectid"; + +interface ObjectIDInstance { + toHexString(): string; +} + +/** CJS interop: tsgo resolves the default as the module namespace, not the callable. */ +const ObjectID = _ObjectID as unknown as () => ObjectIDInstance; + +/** + * Generates a MongoDB-compatible ObjectId as a 24-character lowercase hex string. + * Uses `bson-objectid` which produces time-sortable, globally unique identifiers + * matching MongoDB's native ObjectId format. + */ +export function mdbid(): string { + return ObjectID().toHexString(); +} diff --git a/yarn.lock b/yarn.lock index 6d76a27..a6564e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1019,7 +1019,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=20.0.0, @types/node@npm:>=24": +"@types/node@npm:*, @types/node@npm:>=20.0.0, @types/node@npm:^25.9.1": version: 25.9.1 resolution: "@types/node@npm:25.9.1" dependencies: @@ -1051,66 +1051,66 @@ __metadata: languageName: node linkType: hard -"@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview-darwin-arm64@npm:7.0.0-dev.20260605.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview-darwin-x64@npm:7.0.0-dev.20260605.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview-linux-arm64@npm:7.0.0-dev.20260605.1" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview-linux-arm@npm:7.0.0-dev.20260605.1" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview-linux-x64@npm:7.0.0-dev.20260605.1" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview-win32-arm64@npm:7.0.0-dev.20260605.1" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview-win32-x64@npm:7.0.0-dev.20260605.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@typescript/native-preview@npm:^7.0.0-dev.20260603.1": - version: 7.0.0-dev.20260603.1 - resolution: "@typescript/native-preview@npm:7.0.0-dev.20260603.1" +"@typescript/native-preview@npm:^7.0.0-dev.20260605.1": + version: 7.0.0-dev.20260605.1 + resolution: "@typescript/native-preview@npm:7.0.0-dev.20260605.1" dependencies: - "@typescript/native-preview-darwin-arm64": "npm:7.0.0-dev.20260603.1" - "@typescript/native-preview-darwin-x64": "npm:7.0.0-dev.20260603.1" - "@typescript/native-preview-linux-arm": "npm:7.0.0-dev.20260603.1" - "@typescript/native-preview-linux-arm64": "npm:7.0.0-dev.20260603.1" - "@typescript/native-preview-linux-x64": "npm:7.0.0-dev.20260603.1" - "@typescript/native-preview-win32-arm64": "npm:7.0.0-dev.20260603.1" - "@typescript/native-preview-win32-x64": "npm:7.0.0-dev.20260603.1" + "@typescript/native-preview-darwin-arm64": "npm:7.0.0-dev.20260605.1" + "@typescript/native-preview-darwin-x64": "npm:7.0.0-dev.20260605.1" + "@typescript/native-preview-linux-arm": "npm:7.0.0-dev.20260605.1" + "@typescript/native-preview-linux-arm64": "npm:7.0.0-dev.20260605.1" + "@typescript/native-preview-linux-x64": "npm:7.0.0-dev.20260605.1" + "@typescript/native-preview-win32-arm64": "npm:7.0.0-dev.20260605.1" + "@typescript/native-preview-win32-x64": "npm:7.0.0-dev.20260605.1" dependenciesMeta: "@typescript/native-preview-darwin-arm64": optional: true @@ -1128,7 +1128,7 @@ __metadata: optional: true bin: tsgo: bin/tsgo.js - checksum: 10c0/5fef1e00758766bd434991675c3f2fe127cb319be9e14d128e9a59a67b481e55ea80126adee26896974c50da66bf3a25255bb3ae74452d9cc8e18af68340aa6a + checksum: 10c0/43f5ccb2325db502d0887d5a09a254b5943ae59b7491d0dd1ff10cb8317763db106ba602c9a46c7bafee07f4155ac70f3e9bcdbe31deccdc588fca00b6c6e2a4 languageName: node linkType: hard @@ -1252,11 +1252,12 @@ __metadata: resolution: "@webiny/stdlib@workspace:." dependencies: "@changesets/cli": "npm:^2.31.0" - "@types/node": "npm:>=24" - "@typescript/native-preview": "npm:^7.0.0-dev.20260603.1" + "@types/node": "npm:^25.9.1" + "@typescript/native-preview": "npm:^7.0.0-dev.20260605.1" "@vitest/coverage-v8": "npm:^4.1.8" "@webiny/di": "npm:^1.0.1" adio: "npm:^3.0.1" + bson-objectid: "npm:^2.0.4" dot-prop: "npm:^10.1.0" fast-glob: "npm:^3.3.3" happy-dom: "npm:^20.10.1" @@ -1387,6 +1388,13 @@ __metadata: languageName: node linkType: hard +"bson-objectid@npm:^2.0.4": + version: 2.0.4 + resolution: "bson-objectid@npm:2.0.4" + checksum: 10c0/9dbe55a8cd5ed5ddae0c971429a34caadcf38bc5b98070302e740be2e3c04ab2280925c8f4cba28c88e13f51018dff7d674c610370847286e27e6d76c8990a3f + languageName: node + linkType: hard + "buffer-image-size@npm:^0.6.4": version: 0.6.4 resolution: "buffer-image-size@npm:0.6.4" @@ -2217,8 +2225,8 @@ __metadata: linkType: hard "node-gyp@npm:latest": - version: 12.3.0 - resolution: "node-gyp@npm:12.3.0" + version: 12.4.0 + resolution: "node-gyp@npm:12.4.0" dependencies: env-paths: "npm:^2.2.0" exponential-backoff: "npm:^3.1.1" @@ -2232,7 +2240,7 @@ __metadata: which: "npm:^6.0.0" bin: node-gyp: bin/node-gyp.js - checksum: 10c0/9d9032b405cbe42f72a105259d9eb679376470c102df4a2dbaa51e07d59bf741dcffb85897087ea9d8318b9cabb824a8978af51508ae142f0239ae1e6a3c2329 + checksum: 10c0/9acb7c798e124275a6f9c1f7eb64b5abd6196bb885a3945fb44ee0dccf435514e88cdfb0f228ee7ff76ef25107c1f39ff37a067bf92fd00b9aff9234db29ff9e languageName: node linkType: hard @@ -2248,9 +2256,9 @@ __metadata: linkType: hard "obug@npm:^2.1.1": - version: 2.1.1 - resolution: "obug@npm:2.1.1" - checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78 + version: 2.1.2 + resolution: "obug@npm:2.1.2" + checksum: 10c0/e857080ef23c018bd4ad8553238cd97008cd4ab8472b4b83eafe75c19e9b6f84ac8fe53ef7f50b515a1dbf83e6372dd0b198aece33bc716edfa70366f6f6ed5e languageName: node linkType: hard @@ -2905,11 +2913,11 @@ __metadata: linkType: hard "semver@npm:^7.3.5, semver@npm:^7.5.3": - version: 7.8.1 - resolution: "semver@npm:7.8.1" + version: 7.8.2 + resolution: "semver@npm:7.8.2" bin: semver: bin/semver.js - checksum: 10c0/92d6871d6347e1f99d0ba396a70f2545ccf2a032cda3d378fa0699edf7506b5c6d266aed55c8b88e72bd91a30d2351e4f39db479375374430fcdc4b58f4e3c1a + checksum: 10c0/8e8c193fa75b938e5b3ccf6707c6447e4b34f73e493e72b03f3185393489f45e049144052f624217c346d6c6e0a301dda8eeab2f14413e337218ecb1cbd2de16 languageName: node linkType: hard