sangkan / wiji
Wiji (ꦮꦶꦗꦶ) — Optimized Unique Identifier. 128-bit, µs-precision, monotonic, multi-format. By Sangkan.
Requires
- php: >=7.3
README
Wiji adalah 128-bit time-ordered identifier (timestamp-first) untuk sistem yang butuh urutan yang rapi di database, monotonic generator, dan ekstraksi waktu presisi microsecond (µs).
Website docs: wiji.sangkan.dev.
Packages
- JavaScript / TypeScript (npm):
js/→@sangkan-dev/wiji - PHP (Composer):
sangkan/wiji— manifestcomposer.jsondi root repo (autoload kephp/src/).
Quick start
- JS/TS: lihat
js/README.md - PHP: lihat
php/src/Wiji.phpdanphp/tests/WijiTest.php. Untuk kontributor: dari root repo jalankancomposer installlalucomposer test.
Forged at Sangkan — Building the Source.
Deploy (Cloudflare Pages)
Website lives in site/. Deployment notes: site/DEPLOY_CLOUDFLARE_PAGES.md.
Mengapa Wiji?
| Properti | UUID v4 | UUID v7 | ULID | KSUID | Wiji v1 |
|---|---|---|---|---|---|
| Presisi timestamp | — | 1 ms | 1 ms | 1 detik | 1 µs |
| B+ tree friendly | ❌ | ✅ | ✅ | ✅ | ✅ |
| Monotonic | ❌ | Sebagian | Sebagian | ❌ | ✅ |
| Version field | ✅ | ✅ | ❌ | ❌ | ✅ |
| Binary output | ✅ | ✅ | ❌ | ❌ | ✅ |
| Zero dependencies | ❌ | ❌ | ❌ | ❌ | ✅ |
| String length | 36 | 36 | 26 | 27 | 26 |
| Valid hingga | — | 10889 | 10889 | 2106 | 4253 |
Bit Layout
127 0
┌──────────────────────────┬────────────────┬────┬─────────────────────────────────┐
│ timestamp_us │ sequence │ver │ random │
│ 56 bits │ 16 bits │ 4b │ 52 bits │
└──────────────────────────┴────────────────┴────┴─────────────────────────────────┘
- 56-bit timestamp µs — microseconds sejak Unix epoch. Valid hingga tahun 4253.
- 16-bit sequence — counter monotonic per µs, 0–65535. Overflow → tunggu µs berikutnya.
- 4-bit version — selalu
0x1untuk Wiji v1. Future-proof untuk evolusi spec. - 52-bit random — CSPRNG, di-generate sekali per generator instance. Isolasi antar proses.
Quick Start
JavaScript / Node.js / Bun / Deno
npm install @sangkan-dev/wiji
import wiji from '@sangkan-dev/wiji'; // atau: const wiji = require('@sangkan-dev/wiji'); // Generate wiji() // → '01JKM5WXR9P003K1F4Q8XTBZN2' wiji.binary() // → Uint8Array(16) wiji.uuid() // → '019d2257-972f-1a4e-36ea-81d64ed08dfc' wiji.hex() // → '019d2257972f1a4e36ea81d64ed08dfc' // Parse wiji.parse('01JKM5WXR9P003K1F4Q8XTBZN2') // → { timestamp_us: 1774397000000000, timestamp_ms: 1774397000000, // date: Date, sequence: 42, version: 1, random: Uint8Array(7) } // Utilities wiji.isValid('01JKM5WXR9P003K1F4Q8XTBZN2') // → true wiji.timestampUs('01JKM5WXR9P003K1F4Q8XTBZN2') // → microseconds wiji.compare(a, b) // → -1 | 0 | 1 wiji.factory() // → isolated generator instance
PHP / Laravel
composer require sangkan/wiji
use Sangkan\Wiji\Wiji; $wiji = new Wiji(); // Generate $wiji->generate(); // → '01JKM5WXR9P003K1F4Q8XTBZN2' $wiji->generateBinary(); // → 16 raw bytes (string) $wiji->generateUuid(); // → '019d2257-972f-1a4e-36ea-81d64ed08dfc' $wiji->generateHex(); // → '019d2257972f1a4e36ea81d64ed08dfc' // Parse Wiji::parse('01JKM5WXR9P003K1F4Q8XTBZN2'); // → ['timestamp_us' => ..., 'timestamp_ms' => ..., 'datetime' => DateTimeImmutable, ...] // Utilities Wiji::isValid('01JKM5WXR9P003K1F4Q8XTBZN2'); // → true Wiji::timestampUs($id); // → int (microseconds) Wiji::compare($a, $b); // → -1 | 0 | 1
Laravel — Eloquent integration
// database/migrations/xxxx_create_posts_table.php Schema::create('posts', function (Blueprint $table) { $table->string('id', 26)->primary(); // atau binary: // $table->binary('id', 16)->primary(); $table->string('title'); $table->timestamps(); }); // app/Models/Post.php use Sangkan\Wiji\Wiji; class Post extends Model { protected $keyType = 'string'; public $incrementing = false; protected static function boot(): void { parent::boot(); static::creating(function (self $model) { if (empty($model->{$model->getKeyName()})) { $wiji = new Wiji(); $model->{$model->getKeyName()} = $wiji->generate(); } }); } }
Database Storage
| Database | Column type | Notes |
|---|---|---|
| MySQL / MariaDB | VARCHAR(26) |
Primary, natural sort |
| MySQL / MariaDB | BINARY(16) |
Compact, fastest index |
| PostgreSQL | CHAR(26) atau UUID |
UUID format via generateUuid() |
| SQLite | TEXT |
Default |
| MongoDB | Store as string | Natural sort preserved |
Performance
Measured on Node.js 22, PHP 8.3 (single core):
| IDs/sec | |
|---|---|
| Wiji JS (string) | ~800k |
| Wiji JS (binary) | ~3.5M |
| Wiji PHP (string) | ~290k |
| UUID v4 (JS) | ~1.7M |
| ULID (JS npm) | ~4k |
Wiji binary output adalah yang tercepat untuk pipeline yang pakai binary storage.
Implementasi
| Bahasa | Package | Status |
|---|---|---|
| JavaScript / TypeScript | @sangkan-dev/wiji |
✅ Stable |
| PHP | sangkan/wiji |
✅ Stable |
| Python | sangkan-wiji |
🔜 Coming soon |
| Go | github.com/sangkan-dev/wiji-go |
🔜 Coming soon |
| Rust | wiji (crates.io) |
🔜 Coming soon |
Composer (Packagist) — rilis
composer.json untuk paket sangkan/wiji berada di root monorepo ini agar Packagist bisa membacanya.
- Commit & push perubahan ke GitHub (
sangkan-dev/wiji). - Buat tag semver dan push, contoh:
git tag v1.0.0lalugit push origin v1.0.0— versi di Composer mengikuti tag (vdi depan opsional; Packagist umumnya mengenaliv1.0.0/1.0.0). - Di Packagist: Submit → masukkan URL repo
https://github.com/sangkan-dev/wiji. - Ikuti langkah GitHub webhook di Packagist agar tag/commit baru terindeks otomatis.
Setelah terdaftar, pengguna memasang dengan: composer require sangkan/wiji.
Specification
Lihat WIJI_SPEC.md untuk spesifikasi lengkap, termasuk bit layout, byte layout, test vectors, dan panduan implementasi di bahasa lain.
Spec dirilis di bawah CC0 1.0 — public domain. Siapapun boleh mengimplementasikan Wiji tanpa batasan.
License
MIT — lihat LICENSE
Forged at Sangkan — Building the Source. Inspired by "Sangkan Paraning Dumadi" — memahami asal dan tujuan dari segala ciptaan.