@rsmlamongan/user-menu 0.0.0-stage → 0.1.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/LICENSE +61 -0
- package/README.md +273 -2
- package/dist/components/UserMenuHost.vue.d.ts +33 -0
- package/dist/errors.d.ts +6 -0
- package/dist/http.d.ts +17 -0
- package/dist/http.js +70 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +631 -0
- package/dist/mock.d.ts +11 -0
- package/dist/mock.js +58 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/state.d.ts +56 -0
- package/dist/style.css +2 -0
- package/dist/types.d.ts +52 -0
- package/dist/validation-ZwudKvW7.js +49 -0
- package/dist/validation.d.ts +5 -0
- package/docs/API-CONTRACT.md +221 -0
- package/docs/DEVELOPMENT.md +258 -0
- package/docs/RELEASING.md +114 -0
- package/package.json +92 -4
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Panduan Development RSML User Menu
|
|
2
|
+
|
|
3
|
+
Frontend menyediakan drawer pengguna, favorit pribadi lintas aplikasi, mock development, dan adapter HTTP. Autentikasi tetap dikelola aplikasi utama. Backend tersedia dalam source kepegawaian-api; penerapan schema serta integrasi produksi perlu diverifikasi pada environment tujuan.
|
|
4
|
+
|
|
5
|
+
Identitas package: `@rsmlamongan/user-menu@0.0.0`, `private: false`, dengan `license: "SEE LICENSE IN LICENSE"`. [Lisensi proprietary](../LICENSE) membatasi penggunaan untuk RSML. Contoh integrasi pada [README](../README.md#integrasi) memakai ekspor runtime yang sudah tersedia; publikasi registry tetap memerlukan bootstrap maintainer.
|
|
6
|
+
|
|
7
|
+
[Kembali ke README](../README.md) | [Kontrak API](API-CONTRACT.md)
|
|
8
|
+
|
|
9
|
+
## Struktur dan tooling
|
|
10
|
+
|
|
11
|
+
Konfigurasi berasal dari `rsml-boilerplate-web`, lalu disesuaikan sebagai library. Pola TypeScript, semicolon, double quote, import sorting, Bun, Oxlint/ESLint, dan Prettier tim dipertahankan. Tidak ada router, Pinia, HTTP client, environment produksi aplikasi, Tailwind, font/ikon tambahan, atau plugin auto-import.
|
|
12
|
+
|
|
13
|
+
| Lokasi | Tanggung jawab |
|
|
14
|
+
| ---------------------------------------------- | --------------------------------------------------------------------------- |
|
|
15
|
+
| `src/types.ts`, `errors.ts` | Kontrak publik data, integrasi, dan error. |
|
|
16
|
+
| `src/plugin.ts`, `state.ts` | Injeksi konfigurasi, state privat host, lifecycle sesi, request, dan draft. |
|
|
17
|
+
| `src/components/UserMenuHost.vue`, `style.css` | Drawer, dialog, interaksi, dan CSS dengan namespace komponen. |
|
|
18
|
+
| `src/mock.ts` | Adapter development persisten; hanya diekspor melalui `./mock`. |
|
|
19
|
+
| `src/__tests__/` | Unit test mock/runtime/komponen dengan Vitest, Vue Test Utils, jsdom. |
|
|
20
|
+
| `playground/` | Aplikasi contoh Vue + Vuetify dengan data sintetis. |
|
|
21
|
+
| `e2e/`, `playwright*.ts` | Chromium desktop/mobile untuk development dan production. |
|
|
22
|
+
| `scripts/verify-package.mjs` | Audit tarball dan consumer Vite terpisah. |
|
|
23
|
+
|
|
24
|
+
Vite library menghasilkan `dist/index.js`, `mock.js`, `style.css`, dan chunk internal bersama; `vue-tsc` menghasilkan deklarasi tipe. Build deklarasi dijalankan setelah Vite membersihkan output. Vue/Vuetify beserta seluruh subpath dieksternalkan. CSS ditandai pada `sideEffects` dan diimpor eksplisit oleh consumer melalui `@rsmlamongan/user-menu/style.css`.
|
|
25
|
+
|
|
26
|
+
Konfigurasi Vite playground dan Vitest terpisah dari build library. Tes dikeluarkan dari deklarasi. Manifest mengizinkan hanya `dist/` dan `docs/` ditambah README, manifest, dan LICENSE; source development, dependency lokal, hasil playground, dan kredensial tidak ikut tarball.
|
|
27
|
+
|
|
28
|
+
Bun menjadi package manager development dengan satu `bun.lock`. Gunakan Node `^22.18.0 || >=24.12.0` dan Bun 1.4.2.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bun install --frozen-lockfile
|
|
32
|
+
bun run dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Dev server ada di `http://127.0.0.1:3000`; `bun run preview` melayani hasil build playground di `http://127.0.0.1:4173`.
|
|
36
|
+
|
|
37
|
+
## Pembagian tanggung jawab
|
|
38
|
+
|
|
39
|
+
| Bagian | Tanggung jawab |
|
|
40
|
+
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| Aplikasi utama | Memasang plugin dan satu host, menghubungkan avatar, menyediakan sesi reaktif, metadata halaman, pengaturan, dan adapter yang menggunakan auth aplikasi. |
|
|
42
|
+
| Library | UI drawer/dialog, state dan draft per aplikasi, validasi frontend, pemanggilan adapter, loading/error, serta pengabaian hasil sesi lama. |
|
|
43
|
+
| API bersama (berikutnya) | Memverifikasi sesi, menentukan pemilik, menyimpan daftar/urutan, memeriksa kepemilikan, dan transaksi atomik. |
|
|
44
|
+
| Mock | Menirukan tiga operasi adapter memakai data sintetis dan penyimpanan lokal per pengguna. |
|
|
45
|
+
|
|
46
|
+
Target peer Vue `>=3.5.42 <4` dan Vuetify `>=4.2.0 <5`. Verifikasi menggunakan versi terkunci, bukan seluruh versi dalam rentang peer. Library mengimpor komponen Vuetify eksplisit, menggunakan konteks `v-app`/Vuetify host, serta tidak membuat instance Vuetify, router, state manager, atau penyimpanan token.
|
|
47
|
+
|
|
48
|
+
## Alur drawer
|
|
49
|
+
|
|
50
|
+
Temporary drawer di kanan mempunyai lebar 360 piksel dengan batas 100vw dan tidak menggeser konten aplikasi. Slot header opsional berada sebelum daftar favorit, sedangkan footer aksi berada di luar area scroll. Font, warna, dan token mengikuti aplikasi utama; CSS tambahan dibatasi pada namespace `rsml-user-menu`.
|
|
51
|
+
|
|
52
|
+
### Mode normal
|
|
53
|
+
|
|
54
|
+
1. Avatar mengubah boolean yang terhubung melalui `<UserMenuHost v-model="menuOpen" />`. Sesi kosong mengoreksi model ke `false` tanpa membuka menu.
|
|
55
|
+
2. Tanpa prop `items`, setiap pembukaan meminta daftar terbaru. Pembukaan berulang/request yang masih berjalan tidak menghasilkan request ganda. Dengan `items`, host membaca props tanpa `listFavorites()`.
|
|
56
|
+
3. Loading, daftar kosong, dan error merupakan state berbeda. Error load menyediakan **Coba lagi**. Loading/error loader eksternal dikelola parent.
|
|
57
|
+
4. Favorit menggunakan anchor browser tanpa target baru, sehingga navigasi default berada di tab yang sama dan tidak memerlukan router.
|
|
58
|
+
5. **Tambahkan halaman ini**, **Edit favorit**, serta pengaturan tersedia di dalam drawer.
|
|
59
|
+
|
|
60
|
+
### Tambah halaman aktif
|
|
61
|
+
|
|
62
|
+
Metadata terbaru diambil dari `getCurrentPage()` saat tombol diklik. Dialog menyediakan judul yang dapat diedit dan pratinjau URL yang tidak dapat diedit. `appId` berasal dari konfigurasi plugin.
|
|
63
|
+
|
|
64
|
+
Judul kosong, URL selain HTTP(S), URL relatif, kredensial dalam URL, serta URL duplikat ditolak. Normalisasi menggunakan parser `URL`, tanpa membuang query/hash. Adapter asli tetap wajib memvalidasi duplikasi dan input di server.
|
|
65
|
+
|
|
66
|
+
Sukses menambahkan hasil ke urutan terakhir. Kegagalan mempertahankan judul dan dialog. Pengiriman ganda ditahan selama mutasi. Tombol **Batal** membuang form; Escape atau klik di luar dialog yang mempunyai input meminta konfirmasi buang/lanjutkan.
|
|
67
|
+
|
|
68
|
+
### Mode edit
|
|
69
|
+
|
|
70
|
+
1. **Edit favorit** membuat salinan daftar tersimpan sebagai draft; link navigasi tidak dirender.
|
|
71
|
+
2. Hapus hanya mengubah draft. Native drag-and-drop serta tombol naik/turun mengubah urutan lokal; tombol menjadi alternatif keyboard dan perangkat sentuh.
|
|
72
|
+
3. **Simpan** mengirim satu `saveFavoriteChanges({ orderedIds, deletedIds })` dan mengganti state tersimpan dengan hasil adapter.
|
|
73
|
+
4. **Batal** membuang draft tanpa mutasi API.
|
|
74
|
+
5. Simpan gagal mempertahankan draft beserta pesan error. Pengguna dapat mencoba lagi atau membatalkan; konflik memerlukan Batal lalu pemuatan daftar terbaru melalui API atau loader parent dalam mode `items`.
|
|
75
|
+
|
|
76
|
+
Tambah favorit dan pengaturan ditahan selama edit. Refresh sumber daftar eksternal mengikuti pemeriksaan konflik pada bagian integrasi di bawah. Perubahan draft, pengiriman ganda, serta penutupan ditahan selama mutasi. Penutupan drawer dengan draft berubah menampilkan **Buang perubahan** atau **Lanjutkan edit**. Draft tanpa perubahan dapat langsung ditutup. Logout/pergantian sesi selalu membersihkan UI, termasuk saat request masih berjalan.
|
|
77
|
+
|
|
78
|
+
Mode edit MVP hanya mengatur hapus dan urutan. Judul favorit yang sudah tersimpan tidak dapat diubah; tidak ada editor URL, folder, atau formulir akun.
|
|
79
|
+
|
|
80
|
+
### Slot header dan footer
|
|
81
|
+
|
|
82
|
+
`UserMenuHost` menyediakan slot header opsional tanpa props dan footer dengan props `openSettings: () => Promise<void>` serta `settingsDisabled: boolean`. Deklarasinya ikut dihasilkan pada build tipe. Contoh tersedia pada [README](../README.md#slot-header-dan-footer).
|
|
83
|
+
|
|
84
|
+
- Header bawaan identitas tidak dirender. Tanpa slot header, tidak ada wrapper, padding, atau pembatas tambahan. Konten header kustom tampil sebelum bagian favorit di area scroll.
|
|
85
|
+
- Tombol tutup berada sejajar Favorit saya/Edit favorit dan menerima fokus awal. Drawer mempunyai nama aksesibel Menu pengguna melalui aria-label.
|
|
86
|
+
- Footer memakai append VNavigationDrawer sehingga tetap di dasar drawer saat daftar digulir. Footer kustom menggantikan fallback Pengaturan; tanpa slot kustom, opsi settings menampilkan fallback di area yang sama. Tanpa slot maupun settings, area tidak dirender.
|
|
87
|
+
- settingsDisabled bernilai true saat tidak ada sesi/provider, sedang edit, atau operasi berlangsung. openSettings mengikuti guard yang sama, menunggu callback async, dan menampilkan pesan umum bila gagal. Logout merupakan tanggung jawab aplikasi pemakai.
|
|
88
|
+
- Slot tetap tersedia pada mode normal, edit, loading, dan error. Consumer yang tidak membaca slot props tetap kompatibel. Wrapper mengikuti token tema menu.
|
|
89
|
+
|
|
90
|
+
Kontrol slot mengikuti focus trap drawer. Tab/Shift+Tab mengikuti urutan DOM termasuk footer, sedangkan fokus awal tetap pada tombol tutup. Escape mengikuti konfirmasi draft dan mengembalikan fokus ke pemicu setelah penutupan diterima. Playground menyediakan header opsional dan dua tombol Pengaturan/Bantuan sejajar pada footer.
|
|
91
|
+
|
|
92
|
+
Tes memeriksa header kosong, fallback/replacement footer, pembatasan Pengaturan, posisi footer saat scroll, overflow, serta keyboard/fokus pada desktop/mobile dan tema terang/gelap. Consumer tarball memeriksa tipe slot props, build, dan smoke browser.
|
|
93
|
+
|
|
94
|
+
### Fokus dan aksesibilitas
|
|
95
|
+
|
|
96
|
+
Drawer/dialog mempunyai label, fokus awal, focus trap, serta dukungan Escape. Kontrol hapus/urutan mempunyai nama berdasarkan judul favorit. Perubahan urutan dan hasil mutasi diumumkan melalui status live. Drawer tertutup bersifat inert dan ditandai tersembunyi.
|
|
97
|
+
|
|
98
|
+
Dialog aktif menangani Escape lebih dahulu. Setelah drawer ditutup, fokus kembali ke elemen pemicu jika masih ada. Avatar host harus berupa tombol yang dapat difokuskan. Tombol yang memerlukan data dinonaktifkan sampai load berhasil; daftar kosong mengizinkan tambah jika provider create tersedia.
|
|
99
|
+
|
|
100
|
+
## Kontrak integrasi aplikasi
|
|
101
|
+
|
|
102
|
+
Ekspor utama adalah `createUserMenu`, `UserMenuHost`, `UserMenuError`, serta seluruh tipe pada [src/types.ts](../src/types.ts). Subpath `./mock` mengekspor factory beserta tipe opsinya; `./style.css` menyediakan stylesheet. Mock tidak dipasang otomatis oleh plugin.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
interface UserMenuSession {
|
|
106
|
+
key: string;
|
|
107
|
+
user: { id: string; name: string; avatarUrl?: string };
|
|
108
|
+
}
|
|
109
|
+
interface CurrentPage {
|
|
110
|
+
title: string;
|
|
111
|
+
url: string;
|
|
112
|
+
}
|
|
113
|
+
type UserMenuSettings =
|
|
114
|
+
| { label: string; url: string; onClick?: never }
|
|
115
|
+
| { label: string; onClick: () => void | Promise<void>; url?: never };
|
|
116
|
+
interface UserMenuOptions {
|
|
117
|
+
appId: string;
|
|
118
|
+
getSession: () => UserMenuSession | null;
|
|
119
|
+
getCurrentPage: () => CurrentPage;
|
|
120
|
+
api?: UserMenuApi;
|
|
121
|
+
settings?: UserMenuSettings;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`UserMenuApi` mengikuti [kontrak adapter](API-CONTRACT.md#kontrak-adapter-asynchronous). Plugin hanya menyediakan opsi melalui provide/inject. Pasang tepat satu `UserMenuHost` di dalam `v-app` yang ada, dengan boolean reaktif yang diinisialisasi `ref(false)`.
|
|
126
|
+
|
|
127
|
+
### Sumber daftar melalui prop items
|
|
128
|
+
|
|
129
|
+
Host menerima `items?: readonly Favorite[]` dan mengirim `update:items` dengan payload `Favorite[]`. Contoh lengkap pada [README](../README.md#daftar-dari-aplikasi-melalui-props); `v-model:items="menuItems"` merupakan singkatan `:items="menuItems" @update:items="menuItems = $event"`. Binding visibilitas `v-model` tetap wajib dan terpisah dari daftar.
|
|
130
|
+
|
|
131
|
+
| Nilai `items` | Sumber daftar |
|
|
132
|
+
| -------------------------------- | ---------------------------------------------- |
|
|
133
|
+
| Tidak diberikan atau `undefined` | `api.listFavorites()` setiap pembukaan. |
|
|
134
|
+
| Array, termasuk `[]` | Salinan props, tanpa request daftar oleh host. |
|
|
135
|
+
| Data tidak valid | Error validasi, tanpa fallback ke API. |
|
|
136
|
+
|
|
137
|
+
Array harus berisi **daftar lengkap** favorit pengguna yang sedang login dengan ID stabil serta urutan tersimpan, bukan subset hasil filter/paginasi. Provider mutasi, melalui callback parent atau adapter, harus memakai sumber penyimpanan/pemilik yang sama. Prop `items` mengalihkan pemuatan daftar ke parent; tambah dan Simpan dapat dialihkan secara terpisah melalui callback di bawah. Plugin, sesi, mode edit, pengaturan, slot, dan pemeriksaan visibilitas tetap berlaku.
|
|
138
|
+
|
|
139
|
+
Saat pembukaan, salin dan validasi props menggunakan aturan ID, judul, URL, appId, serta duplikasi yang sama dengan hasil adapter. Perubahan reaktif array dan field item juga diterapkan. Input, draft, state, dan payload event tidak saling berbagi objek yang dapat dimutasi. Load API yang sudah berjalan ketika `items` mulai diberikan diabaikan, baik sukses maupun gagal. Menghapus prop saat drawer terbuka memuat API; jika ada mutasi, pemuatan menunggu mutasi selesai.
|
|
140
|
+
|
|
141
|
+
Setelah tambah atau Simpan berhasil, perbarui state lokal dan kirim satu `update:items` berisi salinan daftar lengkap hanya pada mode eksternal. Membaca props, Batal, kegagalan, sesi berubah, serta unmount tidak mengirim event tersebut. Parent perlu menyimpan payload untuk mempertahankan hasil pada pembukaan berikutnya; host tidak mengubah props secara langsung dan tidak melakukan write kedua lewat event.
|
|
142
|
+
|
|
143
|
+
Refresh props ketika edit mempertahankan draft beserta snapshot dasar edit. Jika isi/urutan/sumber daftar berubah, Simpan ditolak sebagai `conflict` sebelum pemanggilan adapter; item baru dari parent tidak boleh dianggap sebagai penghapusan draft. Batal mengembalikan tampilan daftar terbaru. Jika props berubah selama mutasi, hasil lama tidak mengganti daftar terbaru maupun mengirim `update:items`; draft/form dipertahankan dengan error konflik. Operasi di adapter mungkin sudah berhasil, sehingga parent perlu melakukan rekonsiliasi melalui loader sendiri. Ini perlindungan UI, bukan transaksi atau pembatalan request backend.
|
|
144
|
+
|
|
145
|
+
Parent menangani loading/error, retry, dan lifecycle loader eksternalnya sendiri. Gunakan `[]` selama belum ada data untuk tetap menonaktifkan pemuatan internal; `undefined` justru memilih API internal. Bersihkan props ketika logout/pergantian akun, tangkap pemilik saat load dimulai, dan abaikan hasil sesi lama di parent. Host tetap menutup serta membersihkan UI pada perubahan sesi dan hanya membaca props terkini ketika dibuka lagi; host tidak dapat memverifikasi pemilik dari objek `Favorite` tanpa identitas pemilik di kontrak data.
|
|
146
|
+
|
|
147
|
+
### Callback create dan save
|
|
148
|
+
|
|
149
|
+
Host menerima `onCreate?: UserMenuCreateHandler` dan `onSave?: UserMenuSaveHandler`. Keduanya callback props, bukan nama dalam `defineEmits`, sehingga return Promise dapat ditunggu langsung. Vue mengompilasi `@create="handleCreate"` menjadi prop `onCreate`; `:on-create="handleCreate"` juga didukung. Gunakan satu handler tanpa modifier event. Tipe handler diekspor dari entry utama:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
type UserMenuCreateHandler = (input: CreateFavoriteInput) => Promise<Favorite>;
|
|
153
|
+
type UserMenuSaveHandler = (input: SaveFavoriteChangesInput) => Promise<Favorite[]>;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Contoh lengkap dan konfigurasi plugin tanpa adapter tersedia pada [README](../README.md#delegasi-tambah-dan-simpan). `getSession`, `getCurrentPage`, dan `appId` tetap wajib. `api` opsional; jika diberikan, interface `UserMenuApi` berisi tiga operasi: `listFavorites`, `createFavorite`, dan `saveFavoriteChanges`.
|
|
157
|
+
|
|
158
|
+
- Pilih provider ketika operasi dimulai: callback untuk operasi itu jika diberikan; jika tidak, metode adapter. Override create tidak mengubah pemilihan save atau pemuatan daftar, dan sebaliknya.
|
|
159
|
+
- Panggil callback sekali dengan salinan input dan tunggu hasilnya. Provider tidak diganti ketika handler berubah selama request; penolakan/throw tidak pernah mencoba provider lain.
|
|
160
|
+
- Validasi dan salin hasil seperti hasil adapter, termasuk URL/ID ganda. Create menambahkan hasil ke akhir; save menerima daftar lengkap terbaru. Payload `update:items` tetap salinan terpisah dan hanya dikirim dalam mode `items` setelah sukses.
|
|
161
|
+
- Loading, larangan pengiriman ganda, penahanan penutupan, pemeliharaan form/draft ketika gagal, serta pembersihan sesi/unmount mengikuti alur yang sama dengan adapter.
|
|
162
|
+
- Parent harus mengembalikan hasil dan meneruskan error; `void` atau hasil tidak valid menjadi error. Parent cukup mengikuti `update:items`, tanpa menulis props selama handler berjalan. Pembaruan props selama mutasi tetap diperiksa sebagai konflik; respons sesi lama/host yang sudah dibuang tidak mengubah UI atau mengirim event.
|
|
163
|
+
- Tanpa provider create, nonaktifkan tombol tambah serta submit dialog. Tanpa provider save, nonaktifkan Edit favorit serta Simpan; Batal tetap tersedia ketika provider hilang saat edit. Guard runtime juga menolak operasi yang tidak tersedia tanpa menghapus draft.
|
|
164
|
+
- Tanpa `items` maupun provider list, tampilkan pesan daftar tidak tersedia dan nonaktifkan retry. Ketersediaan sumber/handler dibaca secara reaktif; tidak ada request palsu, fallback setelah gagal, timeout, atau pembatalan otomatis.
|
|
165
|
+
|
|
166
|
+
### Model visibilitas dan lifecycle host
|
|
167
|
+
|
|
168
|
+
Host memakai `defineModel<boolean>({ required: true })` untuk kontrak `modelValue`/`update:modelValue`; tidak ada default atau mode tanpa binding. API imperatif tidak tersedia. Semua permintaan visibilitas melewati satu transisi internal dengan pemeriksaan sesi, draft, dan mutasi.
|
|
169
|
+
|
|
170
|
+
| Permintaan/kondisi | Hasil |
|
|
171
|
+
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
172
|
+
| `true`, sesi aktif | Buka dan ambil daftar dari props jika tersedia, atau API; permintaan berulang tidak menggandakan load. |
|
|
173
|
+
| Model awal `true` | Diproses setelah mounted. Tanpa sesi, koreksi `false` tanpa load. |
|
|
174
|
+
| `false`, tanpa draft berubah/mutasi | Tutup drawer. |
|
|
175
|
+
| `false`, draft berubah | Drawer tetap terbuka, model dikoreksi `true`, tampilkan konfirmasi. Buang menghasilkan `false`; lanjutkan mempertahankan `true`. |
|
|
176
|
+
| `false`, mutasi berlangsung | Koreksi `true`; tidak ada antrean tutup otomatis setelah mutasi selesai. |
|
|
177
|
+
| Logout/pergantian key sesi atau ID | Bersihkan UI dan koreksi `false`, termasuk ketika sibuk atau permintaan buka parent belum diterapkan. |
|
|
178
|
+
|
|
179
|
+
Tombol tutup, Escape, dan scrim menggunakan transisi yang sama. Sinkronisasi membandingkan nilai untuk menghindari loop; koreksi sesi juga membatalkan permintaan parent yang masih tertunda. Parent dapat melihat perubahan singkat `false` lalu `true` ketika penutupan ditolak; assignment `false` bukan bukti penutupan telah diterima.
|
|
180
|
+
|
|
181
|
+
Host membuat state privat ketika setup dan membuangnya sebelum unmount: hentikan sinkronisasi model serta listener keyboard, invalidasi respons lama, dan hentikan scope sesi. Cleanup tidak mengubah model parent atau memulihkan fokus setelah host sudah dihancurkan. Remount membuat state baru dan memproses model parent yang masih berlaku. Unmount aplikasi otomatis membuang host; plugin tidak menyimpan state UI.
|
|
182
|
+
|
|
183
|
+
### Sesi pengguna
|
|
184
|
+
|
|
185
|
+
`getSession()` harus membaca state auth reaktif (misalnya `ref` atau store aplikasi). `null` menandakan logout. Contoh sintetis: `{ key: "demo-login-1", user: { id: "demo-a", name: "Pengguna Contoh A" } }`.
|
|
186
|
+
|
|
187
|
+
Key membedakan siklus login, termasuk login ulang pengguna yang sama; tetap sama saat refresh token biasa dan berubah pada login baru. Key bukan token dan tidak membuktikan autentikasi ke server.
|
|
188
|
+
|
|
189
|
+
Library mengamati key sesi dan ID pengguna secara sinkron. Perubahannya menutup drawer/dialog, membuang draft, serta membersihkan daftar/error. Setiap operasi membawa generasi sesi; seluruh respons sukses maupun gagal dari generasi lama diabaikan. Interface saat ini tidak menyediakan pembatalan request jaringan. Operasi lama masih dapat selesai di adapter untuk pemilik asal, tetapi hasilnya tidak diterapkan ke UI sesi baru.
|
|
190
|
+
|
|
191
|
+
Perubahan nama/avatar dengan pasangan key/ID yang sama tidak membuang sesi; identitas tidak ditampilkan otomatis oleh host. Adapter HTTP berikutnya harus menangkap konteks pemilik/auth saat operasi dimulai dan menggunakan mekanisme refresh/stale-session aplikasi. Token tidak disalin ke konfigurasi library atau mock.
|
|
192
|
+
|
|
193
|
+
### Metadata halaman dan pengaturan
|
|
194
|
+
|
|
195
|
+
Aplikasi menentukan URL absolut, base path, route, query, dan hash yang layak disimpan; jangan memasukkan token atau parameter sesi sementara. Metadata diambil kembali saat dialog tambah dibuka.
|
|
196
|
+
|
|
197
|
+
`settings` opsional menggunakan URL HTTP(S) (URL relatif diselesaikan terhadap halaman host) atau callback `void | Promise<void>`. Aksi tidak disimpan sebagai favorit dan dinonaktifkan saat edit. Form akun/logout tetap milik aplikasi utama. Error callback ditampilkan sebagai pesan umum di drawer.
|
|
198
|
+
|
|
199
|
+
## Mock dan playground
|
|
200
|
+
|
|
201
|
+
[Kontrak API](API-CONTRACT.md#adapter-mock) menjelaskan seluruh opsi `createMockUserMenuApi()`. Mock memenuhi tiga operasi adapter untuk membaca, menambahkan, serta menyimpan penghapusan dan urutan favorit.
|
|
202
|
+
|
|
203
|
+
Satu daftar JSON disimpan per ID pengguna, bukan per `appId`. Seed hanya dipakai ketika key belum ada; daftar kosong tersimpan tidak diisi ulang. Pemilik dan salinan input ditangkap sebelum jeda asynchronous. Hasil selalu salinan, sehingga draft tidak menulis storage secara tidak sengaja.
|
|
204
|
+
|
|
205
|
+
Batch Simpan memvalidasi seluruh ID terbaru sebelum satu penulisan. ID kurang/asing/ganda atau tumpang tindih ditolak sebagai konflik tanpa perubahan parsial. Ini bukan protokol revisi untuk semua kemungkinan penulisan bersamaan antar-tab.
|
|
206
|
+
|
|
207
|
+
Playground menyediakan Ayu/Bima, logout, dua metadata halaman, seed lintas Inventaris/Kepegawaian, callback pengaturan, tema terang/gelap, latensi 0/350/1500 ms, dan kegagalan satu operasi berikutnya. Tombol pergantian akun tertunda membantu menguji respons lama. Semua URL memakai `.example`. Untuk mengulang seed, hapus key `rsml-user-menu:playground:v1:*` dari localStorage melalui devtools.
|
|
208
|
+
|
|
209
|
+
Sakelar **Gunakan items dari aplikasi** default nonaktif. Ketika aktif, playground memuat daftar di parent saat mode/sesi berubah, menyediakan tombol **Muat daftar di aplikasi**, dan menampilkan jumlah item parent. Host menerima array tersebut serta mengembalikan hasil mutasi melalui `update:items`. Loader parent mempunyai pemeriksaan request sendiri untuk mengabaikan respons akun lama.
|
|
210
|
+
|
|
211
|
+
Sakelar **Delegasikan create/save ke parent (tanpa adapter)** juga default nonaktif. Ketika aktif, playground memastikan mode `items` aktif dan opsi plugin tidak menyediakan adapter. Callback parent memakai mock secara eksplisit, mengembalikan hasil Promise, dan menghitung pemanggilan create/save. Menonaktifkan delegasi mengembalikan adapter tanpa menghilangkan contoh daftar eksternal.
|
|
212
|
+
|
|
213
|
+
Penyimpanan terikat origin dan dapat diperiksa/diubah dari browser. Mock tidak membuktikan otorisasi server, sinkronisasi lintas origin, penerimaan token RSML, atau transaksi database produksi.
|
|
214
|
+
|
|
215
|
+
## Verifikasi dan penerimaan
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
bun install --frozen-lockfile
|
|
219
|
+
bun run type-check
|
|
220
|
+
bun run lint:check
|
|
221
|
+
bun run format:check
|
|
222
|
+
bun run test:unit
|
|
223
|
+
bun run test:release
|
|
224
|
+
bun run build
|
|
225
|
+
bun run build:playground
|
|
226
|
+
bun run test:e2e
|
|
227
|
+
bun run test:e2e:production
|
|
228
|
+
bun run verify:package
|
|
229
|
+
git diff --check
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Mesin baru memerlukan `bunx --no-install playwright install chromium`; Linux CI memakai `--with-deps chromium`. `verify:package` memakai `tar`, mengaudit daftar file termasuk lisensi, memasang tarball ke consumer di `.cache/`, memeriksa tipe, membangun Vite, lalu menjalankan alur tambah/Simpan di Chromium. Instalasi consumer memakai seed resolusi dari lockfile proyek. Instalasi pertama menggunakan `--prefer-offline` untuk memakai cache Bun bila tersedia dan mengunduh metadata atau dependency yang belum ada. Instalasi kedua menggunakan `--frozen-lockfile --offline` untuk memeriksa penggunaan ulang hasil instalasi secara terkunci. Package yang diuji tetap dipasang dari tarball lokal. Hasilnya `.cache/npm-package.tgz` dan `.cache/SHA512SUMS`. Tutup server manual pada port tes sebelum menjalankannya.
|
|
233
|
+
|
|
234
|
+
CI menggunakan Ubuntu 24.04, Node 24.19.0, dan Bun 1.4.2. Pemeriksaan dijalankan pada PR/main dan digunakan kembali untuk rilis tag. Hasil E2E dipisahkan ke `test-results/development` dan `test-results/production`; artifact kegagalan disimpan 14 hari. Tes rilis memakai Git temporer, tarball fixture, serta respons registry/GitHub palsu, tanpa publikasi nyata. Detail workflow dan kebutuhan akun ada pada [panduan rilis](RELEASING.md).
|
|
235
|
+
|
|
236
|
+
Consumer mengikuti baseline `@vue/tsconfig` dengan `skipLibCheck: true`. Percobaan `skipLibCheck: false` pada dependency terkunci menemukan error deklarasi bawaan Vuetify 4.2/TypeScript 6 (antara lain `GlobalComponents` dan `Animation.finished`). Verifikasi penggunaan ekspor/tipenya lulus dengan baseline tersebut; pemeriksaan penuh deklarasi dependency eksternal belum lulus. Library tidak menambal tipe pihak ketiga.
|
|
237
|
+
|
|
238
|
+
| Area | Bukti yang disediakan tes |
|
|
239
|
+
| --------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
240
|
+
| Lifecycle | Isolasi antar-aplikasi, logout/login ulang, pergantian pemilik, respons lama, unmount. |
|
|
241
|
+
| Mock | Duplikasi/normalisasi, salinan input/hasil, persistensi, batch atomik, konflik daftar, storage gagal. |
|
|
242
|
+
| UI | Tambah, hapus, urutan, Batal tanpa mutasi, Simpan gagal, hapus seluruh daftar, konfirmasi penutupan. |
|
|
243
|
+
| Props | Array kosong/readonly, perubahan reaktif, tanpa load API, event mutasi, pergantian sumber, draft konflik, respons lama. |
|
|
244
|
+
| Callback | Prioritas independen, tanpa adapter, tanpa fallback saat gagal, provider hilang/berubah, hasil invalid, request lama. |
|
|
245
|
+
| Browser | Desktop/mobile Chromium, development/production, keyboard/fokus, tema, overflow, tombol urutan. Drag native khusus desktop. |
|
|
246
|
+
| Package | Entry utama/mock/CSS/deklarasi, tarball tanpa file development/dependency lokal, consumer Vite terpisah. |
|
|
247
|
+
|
|
248
|
+
Bukti frontend memakai mock dan dependency terkunci. Pengujian ini belum membuktikan kompatibilitas browser lain, semua rentang versi peer, backend, atau aplikasi RSML produksi.
|
|
249
|
+
|
|
250
|
+
## Distribusi dan tahap berikutnya
|
|
251
|
+
|
|
252
|
+
Distribusi menggunakan npm publik dengan nama `@rsmlamongan/user-menu` agar pemasangan Bun/npm tidak memerlukan token registry. Artifact publik dapat dibaca pihak luar; pembatasan penggunaan kode diatur [LICENSE](../LICENSE), sedangkan perlindungan data berada pada API yang memverifikasi sesi dan kepemilikan. Maintainer berwenang tetap mengendalikan publikasi.
|
|
253
|
+
|
|
254
|
+
Workflow rilis memeriksa tag `vX.Y.Z` terhadap versi manifest dan riwayat main, menjalankan seluruh validasi, menerbitkan tarball teruji melalui OIDC, lalu membuat GitHub Release. Ikuti [bootstrap dan setup](RELEASING.md) sebelum push tag rilis pertama. Setiap aplikasi memilih versi pasti, memperbarui lockfile, dan build/deploy sendiri. Tidak ada bundle remote yang otomatis mengganti aplikasi.
|
|
255
|
+
|
|
256
|
+
Adapter HTTP tersedia melalui ekspor `/http`; lihat [kontrak](API-CONTRACT.md#adapter-http). Pengujian mencakup envelope sukses/error, validasi payload, input, serta callback client aplikasi. Tahap berikutnya adalah pemasangan schema dan deployment kepegawaian-api, verifikasi token/origin aplikasi produksi, serta setup akun/publisher dan pembuktian pipeline GitHub/npm. Jangan mengklaim kesiapan produksi dari hasil mock atau pengujian rilis lokal.
|
|
257
|
+
|
|
258
|
+
Referensi: [plugin Vue](https://vuejs.org/guide/reusability/plugins.html), [Vite Library Mode](https://vite.dev/guide/build.html#library-mode), [drag-and-drop native](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Validasi dan rilis package
|
|
2
|
+
|
|
3
|
+
Package publik bernama `@rsmlamongan/user-menu`. Repository `rsmlamongan/rsml-user-menu` tetap private, sehingga GitHub Release mengikuti akses repository. Versi aplikasi pemakai tetap dipilih oleh masing-masing tim; rilis package tidak memperbarui aplikasi secara otomatis.
|
|
4
|
+
|
|
5
|
+
[LICENSE](../LICENSE) adalah lisensi proprietary milik Rumah Sakit Muhammadiyah Lamongan. Package publik dapat diunduh siapa pun, tetapi izin penggunaan terbatas pada aplikasi RSML dan personel yang bertindak atas nama RSML. Penggunaan untuk kepentingan pihak lain memerlukan izin tertulis. Distribusi npm resmi dan bundle JavaScript aplikasi RSML diizinkan. Hak komponen pihak ketiga mengikuti lisensinya masing-masing. Lihat [ketentuan konten npm](https://docs.npmjs.com/policies/open-source-terms/) dan [metadata lisensi](https://docs.npmjs.com/cli/v11/configuring-npm/package-json/#license).
|
|
6
|
+
|
|
7
|
+
## Workflow yang tersedia
|
|
8
|
+
|
|
9
|
+
`ci.yml` berjalan pada PR menuju main, push main, pemanggilan manual, dan pemanggilan reusable oleh workflow rilis. Runner Ubuntu 24.04 memakai Node 24.19.0 dan Bun 1.4.2, dengan instalasi terkunci, type-check, lint, format, unit test, tes guard rilis, kedua build, Chromium desktop/mobile development/production, consumer tarball terpisah, dan pemeriksaan perubahan file tracked. Workflow memakai izin `contents: read`; validasi PR terdahulu dibatalkan jika ada commit pengganti.
|
|
10
|
+
|
|
11
|
+
`verify:package` membuat `.cache/npm-package.tgz` satu kali. Tarball itu diaudit dan dipasang pada consumer, kemudian checksum ditulis ke `.cache/SHA512SUMS`. Artifact `npm-package` baru diunggah setelah seluruh pemeriksaan lulus. Hasil E2E dipisahkan ke `test-results/development` dan `test-results/production`. Artifact kegagalan dan package dipertahankan 14 hari.
|
|
12
|
+
|
|
13
|
+
`release.yml` berjalan ketika tag `v*` di-push. Workflow menolak tag selain stable SemVer `vMAJOR.MINOR.PATCH`, tag yang tidak sama dengan versi manifest, serta commit di luar riwayat main. Validasi berjalan pada commit tag. Setelah lulus, job publish mengunduh artifact tersebut, memeriksa checksum/manifest/lisensi terhadap source tag, lalu menerbitkan tarball dengan npm 11.21.0 melalui OIDC. Tidak ada build atau pack ulang pada job publish.
|
|
14
|
+
|
|
15
|
+
Job GitHub Release berjalan setelah publikasi npm terkonfirmasi dengan integritas yang sama. Catatan perubahan dibuat otomatis oleh GitHub. Release dibuat sebagai draft, kedua asset diunggah, lalu draft dipublikasikan. Pengulangan job memeriksa tag, commit, dan isi asset yang telah tersedia sebelum melanjutkan. Asset terunggah tidak ditimpa; hanya sisa unggahan kosong berstatus `starter` dalam draft yang dibuang untuk dicoba kembali, sesuai [perilaku upload GitHub](https://docs.github.com/en/rest/releases/assets#upload-a-release-asset).
|
|
16
|
+
|
|
17
|
+
Workflow rilis memakai concurrency `npm-release` tanpa membatalkan run yang sedang berlangsung. Jalankan rilis satu per satu dan tunggu selesai sebelum push tag berikutnya; GitHub concurrency dapat mengganti run yang masih pending. Workflow juga menolak publikasi versi baru yang akan menurunkan `latest`. Prerelease dan kenaikan versi otomatis belum didukung.
|
|
18
|
+
|
|
19
|
+
## Akun dan pengaturan awal
|
|
20
|
+
|
|
21
|
+
### GitHub
|
|
22
|
+
|
|
23
|
+
Gunakan akun dengan akses admin repository untuk menyiapkan Actions. Akun yang terhubung saat perencanaan memiliki akses admin; izin aktual tetap mengikuti organisasi.
|
|
24
|
+
|
|
25
|
+
1. Buka repository **Settings → Actions → General**. Pastikan organisasi mengizinkan Actions milik `actions` dan `oven-sh` yang dipakai workflow. Action dikunci ke SHA commit; versi runtime juga ditetapkan eksplisit.
|
|
26
|
+
2. Default workflow permission boleh tetap read-only. Workflow meminta `id-token: write` hanya pada job npm publish dan `contents: write` hanya pada job GitHub Release. Kebijakan organisasi harus mengizinkan permintaan tersebut.
|
|
27
|
+
3. Setelah CI pertama selesai, bila paket GitHub organisasi mendukungnya, atur aturan main agar memerlukan check **Validate**, serta batasi pembuatan/pengubahan/penghapusan tag `v*` kepada maintainer. Jangan memasang required reviewer environment pada workflow ini jika rilis harus otomatis.
|
|
28
|
+
4. Pantau **Actions** dan penggunaan menit/storage pada billing organisasi. Repository private memakai kuota paket organisasi; pemakaian melebihi kuota dapat dikenai biaya. Tidak diperlukan server atau domain tambahan. [Billing GitHub Actions](https://docs.github.com/en/actions/concepts/billing-and-usage)
|
|
29
|
+
|
|
30
|
+
GitHub Release memakai `GITHUB_TOKEN` yang dibuat otomatis oleh Actions; tidak perlu PAT atau secret GitHub tambahan. [Autentikasi GITHUB_TOKEN](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token)
|
|
31
|
+
|
|
32
|
+
### npm dan perangkat lokal
|
|
33
|
+
|
|
34
|
+
1. Buat akun maintainer pada [npm signup](https://www.npmjs.com/signup), lalu verifikasi email. Akun npm ini terpisah dari akun GitHub. [Panduan akun](https://docs.npmjs.com/creating-a-new-npm-user-account/)
|
|
35
|
+
2. Buka profil **Account → Two-Factor Authentication → Enable 2FA**. Gunakan metode yang tersedia, misalnya Windows Hello, dan simpan recovery codes. [Panduan 2FA](https://docs.npmjs.com/configuring-two-factor-authentication/)
|
|
36
|
+
3. Melalui profil, pilih **Add an Organization**, isi `rsmlamongan`, lalu pilih **Unlimited public packages** yang gratis. Jika organisasi tersebut sudah ada dan dikuasai RSML, minta undangan serta izin menerbitkan package. Organisasi GitHub tidak otomatis membuat organisasi npm. Kepemilikan/ketersediaan scope belum diverifikasi oleh pekerjaan ini. [Panduan organisasi](https://docs.npmjs.com/creating-an-org/)
|
|
37
|
+
4. Pastikan Node dan npm tersedia secara lokal untuk bootstrap. Shell development yang diperiksa mempunyai Node 24.19.0 dan Bun 1.4.2, tetapi belum mempunyai perintah npm pada PATH. Pasang distribusi Node yang menyertakan npm, lalu periksa `node --version` dan `npm --version`. Gunakan `npm install --global npm@11.21.0 --ignore-scripts` bila perlu menyamakan CLI publikasi. [Instalasi Node/npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm/)
|
|
38
|
+
|
|
39
|
+
Bun tetap menjadi package manager dependency project, dengan satu `bun.lock`. npm digunakan untuk login/publikasi; tidak perlu menjalankan `npm install` pada root project atau membuat package-lock baru.
|
|
40
|
+
|
|
41
|
+
## Bootstrap satu kali
|
|
42
|
+
|
|
43
|
+
Trusted Publisher memerlukan package yang sudah ada pada registry. Karena itu, publikasi pertama memakai akun maintainer secara interaktif. Ini bukan langkah otomatis dari implementasi workflow. [Persyaratan npm trust](https://docs.npmjs.com/cli/v11/commands/npm-trust/#prerequisites)
|
|
44
|
+
|
|
45
|
+
1. Push commit konfigurasi ke main dan pastikan run CI hijau. Versi bootstrap adalah `0.0.0`; manifest harus sudah bernama `@rsmlamongan/user-menu`, `private: false`, serta memiliki lisensi dan publishConfig yang ditetapkan.
|
|
46
|
+
2. Unduh artifact **npm-package** dari run CI tersebut. Ekstrak ZIP sehingga `npm-package.tgz` dan `SHA512SUMS` berada dalam satu folder. Cocokkan SHA-512 sebelum publikasi. Contoh PowerShell:
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
$expectedHash = (Get-Content ./SHA512SUMS).Split(' ')[0]
|
|
50
|
+
$actualHash = (Get-FileHash ./npm-package.tgz -Algorithm SHA512).Hash.ToLowerInvariant()
|
|
51
|
+
if ($actualHash -ne $expectedHash) { throw 'Checksum tarball berbeda' }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. Dari folder artifact, autentikasi dan publikasikan tarball yang sudah diuji:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
npm login
|
|
58
|
+
npm whoami
|
|
59
|
+
npm publish ./npm-package.tgz --access public --tag bootstrap --ignore-scripts
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Selesaikan permintaan 2FA di browser/perangkat Anda. Versi `0.0.0` berisi library yang diuji, bukan package kosong. Dist-tag `bootstrap` dipakai untuk langkah awal ini. Jika nama/versi sudah tersedia, jangan menimpa atau menerbitkan ulang sebelum memastikan asal dan integritasnya.
|
|
63
|
+
|
|
64
|
+
4. Pada package npm, buka **Settings → Trusted Publisher**, pilih GitHub Actions, lalu isi:
|
|
65
|
+
|
|
66
|
+
| Field | Nilai |
|
|
67
|
+
| -------------------- | ----------------------------------------------- |
|
|
68
|
+
| Organization or user | `rsmlamongan` |
|
|
69
|
+
| Repository | `rsml-user-menu` |
|
|
70
|
+
| Workflow filename | `release.yml` |
|
|
71
|
+
| Environment | Kosong |
|
|
72
|
+
| Allowed actions | Izinkan publikasi langsung dengan `npm publish` |
|
|
73
|
+
|
|
74
|
+
Nama workflow hanya filename, bukan `.github/workflows/release.yml`. Pastikan izin direct publish aktif; izin stage-only akan membutuhkan persetujuan manual dan tidak memenuhi alur otomatis ini.
|
|
75
|
+
|
|
76
|
+
5. Siapkan rilis `0.1.0` dan lakukan langkah pada bagian berikut. Konfigurasi Trusted Publisher baru harus berhasil melakukan publikasi pertamanya dalam dua hari; jika kedaluwarsa, hapus dan buat ulang konfigurasinya. Setelah OIDC terbukti berhasil, aktifkan **Require two-factor authentication and disallow tokens** pada publishing access package. Workflow OIDC tetap bekerja tanpa secret `NPM_TOKEN`. [Pengaturan Trusted Publishing](https://docs.npmjs.com/trusted-publishers/)
|
|
77
|
+
|
|
78
|
+
Trusted Publishing memakai runner GitHub-hosted. Repository ini tetap private, sehingga provenance npm tidak dihasilkan; jangan menganggap OIDC atau checksum sebagai bukti provenance publik. [Batas provenance](https://docs.npmjs.com/trusted-publishers/#automatic-provenance-generation)
|
|
79
|
+
|
|
80
|
+
## Rilis berikutnya
|
|
81
|
+
|
|
82
|
+
1. Ubah `version` dalam package.json, misalnya dari `0.0.0` ke `0.1.0`. Jalankan Bun bila metadata dependency berubah; commit perubahan manifest/lockfile yang relevan.
|
|
83
|
+
2. Push atau merge perubahan ke main dan tunggu CI lulus.
|
|
84
|
+
3. Buat tag pada commit main yang manifestnya mempunyai versi tersebut. Contoh `0.1.0`:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
git switch main
|
|
88
|
+
git pull --ff-only
|
|
89
|
+
git tag -a v0.1.0 -m "Release 0.1.0"
|
|
90
|
+
git push origin v0.1.0
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
4. Pantau workflow **Release**: preflight → validate → publish → github-release. Pastikan versi npm, `latest`, GitHub Release, kedua asset, dan checksum sesuai. Baru setelah itu komunikasikan versi yang tersedia kepada aplikasi pemakai.
|
|
94
|
+
|
|
95
|
+
Aplikasi memilih versi pasti, misalnya `bun add --exact @rsmlamongan/user-menu@0.1.0`, lalu memperbarui lockfile, build, dan deployment masing-masing. Instalasi package publik tidak membutuhkan akun atau token registry. [Package publik npm](https://docs.npmjs.com/about-public-packages/)
|
|
96
|
+
|
|
97
|
+
## Pemulihan kegagalan
|
|
98
|
+
|
|
99
|
+
| Kondisi | Tindakan |
|
|
100
|
+
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
101
|
+
| Preflight atau validasi gagal | Baca log/diagnostik. Perbaiki source dan buat versi/tag baru bila isi rilis berubah. Tidak ada publikasi setelah job yang gagal. |
|
|
102
|
+
| Publikasi ditolak karena auth | Periksa owner, repository, filename, izin direct publish, masa awal konfigurasi, dan runner hosted. Jangan menambahkan token sebagai fallback otomatis. |
|
|
103
|
+
| Registry memberi error jaringan/401/403/429/5xx | Job gagal; error tidak dianggap sebagai izin menerbitkan versi baru. Perbaiki akses atau tunggu layanan pulih, lalu ulangi failed jobs. |
|
|
104
|
+
| Publish sukses tetapi konfirmasi registry belum melihat versinya | Jalankan **Re-run failed jobs**. Pemeriksaan ulang akan melewati publish bila integritas versi yang tersedia sudah cocok. |
|
|
105
|
+
| Versi npm sudah ada dengan integritas sama | Publish dilewati; proses dapat menyelesaikan GitHub Release. Pemulihan versi lama tidak memindahkan `latest` mundur. |
|
|
106
|
+
| Versi/npm asset berbeda dengan artifact | Proses berhenti tanpa overwrite. Selidiki perbedaannya; perubahan isi memerlukan versi baru. |
|
|
107
|
+
| Upload atau publikasi GitHub Release gagal | Gunakan **Re-run failed jobs** pada run yang sama. Draft dan asset yang cocok dilanjutkan. Artifact validasi sebelumnya dipakai kembali; npm tidak dipublikasikan ulang. |
|
|
108
|
+
| Artifact sudah kedaluwarsa | Jangan menganggap build ulang identik. Periksa integritas terhadap versi npm yang sudah tersedia; bila tidak cocok, siapkan versi baru. |
|
|
109
|
+
|
|
110
|
+
Jangan mengubah atau memindahkan tag versi yang sudah diterbitkan. Jika ada kesalahan isi, rilis patch dengan versi baru. Jangan membatalkan workflow yang sedang berada pada tahap publikasi untuk memulai rilis lain.
|
|
111
|
+
|
|
112
|
+
## Batas bukti
|
|
113
|
+
|
|
114
|
+
Pemeriksaan lokal mencakup source, format workflow, fixture Git/tarball, perilaku registry palsu, pemulihan draft/asset GitHub palsu, unit/UI/browser, dan consumer package. Pemeriksaan tersebut tidak membuktikan izin organisasi, kepemilikan scope npm, login/2FA, konfigurasi OIDC, publikasi nyata, atau backend. Catat hasil run GitHub dan tautan versi npm setelah bootstrap/rilis nyata dilakukan.
|
package/package.json
CHANGED
|
@@ -1,6 +1,94 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsmlamongan/user-menu",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Library Vue dan Vuetify untuk menu pengguna RSML.",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rsmlamongan/rsml-user-menu.git"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"registry": "https://registry.npmjs.org/"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"packageManager": "bun@1.4.2",
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": "^22.18.0 || >=24.12.0"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"docs"
|
|
23
|
+
],
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./mock": {
|
|
33
|
+
"types": "./dist/mock.d.ts",
|
|
34
|
+
"import": "./dist/mock.js"
|
|
35
|
+
},
|
|
36
|
+
"./http": {
|
|
37
|
+
"types": "./dist/http.d.ts",
|
|
38
|
+
"import": "./dist/http.js"
|
|
39
|
+
},
|
|
40
|
+
"./style.css": "./dist/style.css"
|
|
41
|
+
},
|
|
42
|
+
"sideEffects": [
|
|
43
|
+
"**/*.css"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "vite --config vite.playground.config.ts",
|
|
47
|
+
"preview": "vite preview --config vite.playground.config.ts",
|
|
48
|
+
"type-check": "vue-tsc --build",
|
|
49
|
+
"lint:check": "run-s lint:check:oxlint lint:check:eslint",
|
|
50
|
+
"lint:check:oxlint": "oxlint .",
|
|
51
|
+
"lint:check:eslint": "eslint .",
|
|
52
|
+
"format": "prettier --write .",
|
|
53
|
+
"format:check": "prettier --check .",
|
|
54
|
+
"build": "run-s type-check build:lib build:types",
|
|
55
|
+
"build:lib": "vite build",
|
|
56
|
+
"build:types": "vue-tsc --build tsconfig.lib.json --force",
|
|
57
|
+
"build:playground": "vite build --config vite.playground.config.ts",
|
|
58
|
+
"test:unit": "vitest run",
|
|
59
|
+
"test:release": "node --test scripts/release.test.mjs",
|
|
60
|
+
"test:unit:watch": "vitest",
|
|
61
|
+
"test:e2e": "playwright test",
|
|
62
|
+
"test:e2e:production": "playwright test --config playwright.config.production.ts",
|
|
63
|
+
"verify:package": "node scripts/verify-package.mjs"
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"vue": ">=3.5.42 <4",
|
|
67
|
+
"vuetify": ">=4.2.0 <5"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@playwright/test": "^1.62.1",
|
|
71
|
+
"@trivago/prettier-plugin-sort-imports": "6.0.2",
|
|
72
|
+
"@tsconfig/node24": "^24.0.5",
|
|
73
|
+
"@types/node": "^24.13.3",
|
|
74
|
+
"@vitejs/plugin-vue": "^6.0.8",
|
|
75
|
+
"@vue/eslint-config-typescript": "^14.9.0",
|
|
76
|
+
"@vue/tsconfig": "^0.9.1",
|
|
77
|
+
"eslint": "^10.9.1",
|
|
78
|
+
"eslint-config-prettier": "^10.1.8",
|
|
79
|
+
"eslint-plugin-oxlint": "~1.73.0",
|
|
80
|
+
"eslint-plugin-vue": "~10.9.2",
|
|
81
|
+
"jiti": "^2.7.0",
|
|
82
|
+
"npm-run-all2": "^9.0.3",
|
|
83
|
+
"oxlint": "~1.74.0",
|
|
84
|
+
"prettier": "3.9.5",
|
|
85
|
+
"typescript": "~6.0.3",
|
|
86
|
+
"vite": "^8.2.2",
|
|
87
|
+
"vue": "^3.5.42",
|
|
88
|
+
"vue-tsc": "^3.3.11",
|
|
89
|
+
"vuetify": "^4.2.0",
|
|
90
|
+
"vitest": "^4.1.11",
|
|
91
|
+
"@vue/test-utils": "^2.5.0",
|
|
92
|
+
"jsdom": "^29.1.1"
|
|
93
|
+
}
|
|
94
|
+
}
|