prpm-dbp 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,6 +134,8 @@ Pengguna kemudian menarik versi baharu dengan `npx skills update`.
134
134
 
135
135
  Pilihan: `--refresh`, `--had <n>`, `--delay <ms>`, `--json`, `--cepat`, `--pedoman`.
136
136
 
137
+ `--luar-talian` tidak pernah menyentuh rangkaian. Wajib untuk laluan permintaan aplikasi: dokumen penuh dari cache mengambil 0.19 saat, manakala satu perkataan baharu daripada PRPM mengambil beberapa saat.
138
+
137
139
  `--pedoman` membenarkan penjanaan imbuhan menurut peraturan DBP apabila kamus tiada jawapan. Ketepatannya diukur 88-98% terhadap 66,000 perkataan sebenar, jadi ia mati secara lalai dan hasilnya sentiasa ditanda.
138
140
 
139
141
  ## Guna sebagai library
package/SKILL.md CHANGED
@@ -69,6 +69,10 @@ Contoh kedua: `dipelajari` ialah terbitan sah daripada `ajar`. Alat ini **tidak*
69
69
 
70
70
  **Bila `rumi` jumpa kekaburan**, satu ejaan Jawi memetakan kepada beberapa perkataan Rumi. Semua calon dipaparkan dipisah `|`. Pilih ikut konteks ayat, jangan ambil yang pertama secara membuta.
71
71
 
72
+ Sebahagian kekaburan itu palsu dan diselesaikan sendiri. DBP menandakan bentuk varian dengan **rujukan silang**: definisi `kalkalah` ialah `®qalqalah.`, bukan definisi sebenar. Varian begitu dinyahutamakan, jadi `قلقله` dibaca `qalqalah` terus.
73
+
74
+ Isyaratnya mestilah tanda `®` itu, **bukan** kehadiran ejaan Jawi. `pada` tiada ejaan Jawi dalam PRPM manakala `pad` (pinjaman Inggeris) ada. Mengutamakan "yang ada Jawi" akan menukar setiap `pada` menjadi `pad`.
75
+
72
76
  Jawi Melayu menulis vokal dengan alif, wau dan ya, tidak seperti Arab. Kerana itu kekaburan jarang berlaku, tetapi ia tetap wujud.
73
77
 
74
78
  `rumi` bekerja dari cache sahaja dan tidak menyentuh rangkaian. Perkataan yang belum pernah disemak dalam arah Rumi ke Jawi tidak akan dikenali. Jalankan `warm` atau `audit` dahulu pada kosa kata yang berkaitan.
@@ -215,12 +219,12 @@ Diuji terhadap 406 perkataan pelajaran Pendidikan Islam Tingkatan 4 sebenar, dit
215
219
 
216
220
  ```
217
221
  token asal 336 -> token pusing balik 336
218
- padan tepat 309 (92%)
219
- padan antara calon 27 (8%)
222
+ padan tepat 314 (93%)
223
+ padan antara calon 22 (7%)
220
224
  TIDAK padan 0 (0%)
221
225
  ```
222
226
 
223
- Dua puluh tujuh kes kabur itu sah: `قلقله` boleh dibaca `qalqalah` atau `kalkalah`, dua ejaan Rumi yang sama-sama betul bagi istilah Arab yang sama. Alat melaporkan kedua-duanya dan tidak memilih.
227
+ Dua puluh dua kes kabur yang tinggal itu sah: `قلقله` boleh dibaca `qalqalah` atau `kalkalah`, dua ejaan Rumi yang sama-sama betul bagi istilah Arab yang sama. Alat melaporkan kedua-duanya dan tidak memilih.
224
228
 
225
229
  **Sifar perkataan hilang atau rosak.**
226
230
 
@@ -233,6 +237,26 @@ Dua puluh tujuh kes kabur itu sah: `قلقله` boleh dibaca `qalqalah` atau `ka
233
237
  | §12 serapan Inggeris | Kamus sudah meliputinya; tiada penjanaan diperlukan. |
234
238
  | §16.2, §18 | Tidak memerlukan kod: perbezaannya sudah dikodkan dalam jarak teks Rumi. Ada ujian yang mengunci tingkah laku ini. |
235
239
 
240
+ ## Menyambung ke aplikasi
241
+
242
+ Kelajuan diukur pada 406 perkataan:
243
+
244
+ | Keadaan | Masa |
245
+ |---|---|
246
+ | Semua dari cache | **0.19 saat** |
247
+ | Dua perkataan baharu (perlu rangkaian) | **30.7 saat** |
248
+
249
+ Perbezaan itu menentukan cara ia disambung. **Dalam laluan permintaan aplikasi, gunakan `--luar-talian` sentiasa.** Mod itu tidak pernah menyentuh rangkaian: perkataan yang tiada dalam cache dilaporkan belum disahkan, bukan diambil secara segerak sambil pengguna menunggu.
250
+
251
+ Urutan yang disyorkan:
252
+
253
+ 1. **Panaskan cache di luar talian** daripada kosa kata projek anda: `npx prpm-dbp warm senarai.txt`
254
+ 2. **Hantar cache itu bersama aplikasi** (salin `~/.cache/prpm-dbp/cache.json`, atau tetapkan `PRPM_CACHE`)
255
+ 3. **Jalankan dengan `--luar-talian`** dalam laluan permintaan
256
+ 4. **Kumpul medan `belumDisahkan`** dan panaskan cache secara berkala di luar talian
257
+
258
+ Dengan cara ini pengguna tidak pernah menunggu PRPM, dan PRPM tidak pernah menerima trafik daripada aplikasi anda.
259
+
236
260
  ## Rantai kepercayaan
237
261
 
238
262
  Setiap ejaan Jawi yang dikeluarkan membawa asal-usulnya. Jangan runtuhkan lapisan ini.
package/bin/cli.js CHANGED
@@ -34,7 +34,8 @@ const BANTUAN = `prpm-dbp — semak Bahasa Melayu dan Jawi terhadap PRPM DBP
34
34
  npx prpm-dbp install [--link] pasang sebagai Claude Code skill
35
35
 
36
36
  Pilihan: --refresh --had <n> --delay <ms> --json --cepat
37
- --pedoman jana imbuhan ikut peraturan DBP (88-98% tepat, hasil ditanda)
37
+ --pedoman jana imbuhan ikut peraturan DBP (88-98% tepat, hasil ditanda)
38
+ --luar-talian jangan sentuh rangkaian langsung (wajib untuk laluan permintaan app)
38
39
 
39
40
  Cache: ${CACHE_PATH}
40
41
  `
@@ -49,6 +50,7 @@ function huraiArgs(argv) {
49
50
  else if (a === '--link') pilihan.link = true
50
51
  else if (a === '--cepat') pilihan.cepat = true
51
52
  else if (a === '--pedoman') pilihan.pedoman = true
53
+ else if (a === '--luar-talian') pilihan.luarTalian = true
52
54
  else if (a === '--jenis') pilihan.jenis = argv[++i]
53
55
  else if (a === '--had') pilihan.had = Number(argv[++i])
54
56
  else if (a === '--delay') pilihan.delay = Number(argv[++i])
@@ -79,7 +81,12 @@ async function main() {
79
81
  }
80
82
 
81
83
  const cache = await loadCache()
82
- const opts = { refresh: pilihan.refresh, delay: pilihan.delay, onProgress: progres }
84
+ const opts = {
85
+ refresh: pilihan.refresh,
86
+ delay: pilihan.delay,
87
+ onProgress: progres,
88
+ luarTalian: pilihan.luarTalian,
89
+ }
83
90
 
84
91
  switch (perintah) {
85
92
  case 'lookup': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm-dbp",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Semak ejaan Bahasa Melayu dan Jawi terhadap PRPM Dewan Bahasa dan Pustaka. CLI dan skill untuk AI agent.",
5
5
  "keywords": [
6
6
  "bahasa-melayu",
package/src/prpm.js CHANGED
@@ -130,11 +130,25 @@ export async function fetchPage(kata, { timeout = 20000 } = {}) {
130
130
 
131
131
  const tidur = (ms) => new Promise((r) => setTimeout(r, ms))
132
132
 
133
- export async function lookup(kata, cache, { refresh = false, delay = 400, perluPenuh = false } = {}) {
133
+ export async function lookup(
134
+ kata,
135
+ cache,
136
+ { refresh = false, delay = 400, perluPenuh = false, luarTalian = false } = {},
137
+ ) {
134
138
  const key = String(kata).trim().toLowerCase()
135
139
  if (!key) return null
136
140
 
137
141
  const simpanan = cache[key]
142
+
143
+ // Mod luar talian TIDAK PERNAH menyentuh rangkaian. Ini wajib untuk
144
+ // laluan permintaan aplikasi: satu perkataan yang belum dicache mengambil
145
+ // beberapa saat, manakala keseluruhan dokumen dari cache mengambil 0.2
146
+ // saat. Perkataan yang tiada dalam cache dilaporkan belum disahkan,
147
+ // bukan diambil secara segerak sambil pengguna menunggu.
148
+ if (luarTalian) {
149
+ if (simpanan) return { ...simpanan, dariCache: true }
150
+ return { kata: key, dijumpai: false, jawi: null, jawiVarian: [], entri: [], nota: 'tiada dalam cache (mod luar talian)', url: `${BASE_URL}?keyword=${encodeURIComponent(key)}` }
151
+ }
138
152
  // Rekod benih membawa ejaan sahaja, tanpa definisi, supaya pakej kekal
139
153
  // kecil. Bila pemanggil betul-betul perlukan definisi, ambil semula.
140
154
  const cukup = simpanan && !(perluPenuh && simpanan.ringkas)
package/src/tukar.js CHANGED
@@ -159,13 +159,28 @@ export function petaSongsang(cache, dataset = null) {
159
159
  const peta = new Map()
160
160
  let maxKata = 1
161
161
 
162
- const tambah = (jawi, rumi) => {
162
+ // DBP menandakan bentuk varian dengan rujukan silang: definisi `kalkalah`
163
+ // ialah "®qalqalah.", bukan definisi sebenar. Varian begitu dinyahutamakan
164
+ // supaya قلقله dibaca qalqalah dan bukan dilaporkan kabur.
165
+ //
166
+ // Isyaratnya mestilah tanda ® itu, BUKAN kehadiran ejaan Jawi. `pada`
167
+ // tiada Jawi dalam PRPM manakala `pad` (pinjaman Inggeris) ada, jadi
168
+ // mengutamakan "yang ada Jawi" akan menukar setiap `pada` menjadi `pad`.
169
+ const rujukanSilang = new Set()
170
+ for (const [kata, rec] of Object.entries(cache)) {
171
+ const def = rec?.entri?.[0]?.definisi ?? ''
172
+ if (/^®/.test(def.trim())) rujukanSilang.add(kata)
173
+ }
174
+
175
+ const tambah = (jawi, rumi, sumber) => {
163
176
  const kunci = normalisasiJawi(jawi)
164
177
  if (!kunci) return
165
178
  const bilangan = kunci.split(/\s+/).length
166
179
  if (bilangan > maxKata) maxKata = bilangan
167
- if (!peta.has(kunci)) peta.set(kunci, [])
168
- if (!peta.get(kunci).includes(rumi)) peta.get(kunci).push(rumi)
180
+ if (!peta.has(kunci)) peta.set(kunci, { utama: [], rujukan: [] })
181
+ const jenis = rujukanSilang.has(rumi) ? 'rujukan' : 'utama'
182
+ const slot = peta.get(kunci)[jenis]
183
+ if (!slot.includes(rumi)) slot.push(rumi)
169
184
  }
170
185
 
171
186
  for (const rec of Object.values(cache)) {
@@ -179,6 +194,19 @@ export function petaSongsang(cache, dataset = null) {
179
194
  return peta
180
195
  }
181
196
 
197
+ /**
198
+ * Pilih calon songsang. Bentuk yang DBP sendiri tandakan sebagai rujukan
199
+ * silang (®) hanya digunakan apabila tiada bentuk utama. Selain itu semua
200
+ * calon dilayan sama rata, dan kekaburan sebenar dilaporkan sepenuhnya.
201
+ */
202
+ export function pilihCalon(masuk) {
203
+ if (!masuk) return null
204
+ if (Array.isArray(masuk)) return { calon: masuk, sumber: 'campur' }
205
+ if (masuk.utama.length) return { calon: masuk.utama, sumber: 'utama' }
206
+ if (masuk.rujukan.length) return { calon: masuk.rujukan, sumber: 'rujukan' }
207
+ return null
208
+ }
209
+
182
210
  /**
183
211
  * Rumi -> Jawi. Pemisah, tanda baca dan baris baru dikekalkan.
184
212
  * Perkataan yang tidak disahkan dibalut penanda, tidak pernah direka.
@@ -360,17 +388,17 @@ export function keRumi(teksJawi, cache, { penanda = ['«', '»'], dataset = null
360
388
  }
361
389
  if (!bolehGabung) continue
362
390
  const frasa = bahagian.slice(i, akhir + 1).join(' ').replace(/\s+/g, ' ')
363
- const jumpa = peta.get(normalisasiJawi(frasa))
364
- if (jumpa) { padanan = jumpa; hujung = akhir; break }
391
+ const jumpa = pilihCalon(peta.get(normalisasiJawi(frasa)))
392
+ if (jumpa) { padanan = jumpa.calon; hujung = akhir; break }
365
393
  }
366
394
 
367
395
  if (!padanan) {
368
396
  // Pedoman DBP §17.1 songsang: kata dasar + ٢ menjadi kata berulang.
369
397
  const dasarUlang = pisahUlangJawi(bahagian[i])
370
398
  if (dasarUlang) {
371
- const jumpaUlang = peta.get(normalisasiJawi(dasarUlang))
372
- if (jumpaUlang?.length === 1) {
373
- keluar.push(jumpaUlang[0] + '-' + jumpaUlang[0])
399
+ const jumpaUlang = pilihCalon(peta.get(normalisasiJawi(dasarUlang)))
400
+ if (jumpaUlang?.calon.length === 1) {
401
+ keluar.push(jumpaUlang.calon[0] + '-' + jumpaUlang.calon[0])
374
402
  continue
375
403
  }
376
404
  }
@@ -379,8 +407,8 @@ export function keRumi(teksJawi, cache, { penanda = ['«', '»'], dataset = null
379
407
  // dalam kamus, jadi cuba pisahkan kata sendi di hadapan.
380
408
  let dipisah = null
381
409
  for (const calon of pisahSendi(bahagian[i])) {
382
- const jumpa = peta.get(normalisasiJawi(calon.baki))
383
- if (jumpa?.length === 1) { dipisah = calon.sendi + ' ' + jumpa[0]; break }
410
+ const jumpa = pilihCalon(peta.get(normalisasiJawi(calon.baki)))
411
+ if (jumpa?.calon.length === 1) { dipisah = calon.sendi + ' ' + jumpa.calon[0]; break }
384
412
  }
385
413
  if (dipisah) { keluar.push(dipisah); sendiDipisah.push(bahagian[i]); continue }
386
414