mnfst 0.5.197 → 0.5.198-next.1

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.
@@ -104,6 +104,32 @@ async function getAppwriteDataServices() {
104
104
  };
105
105
  }
106
106
 
107
+ // Auth pre-flight: one shared account.get() in flight; a pass is cached until auth state changes
108
+ let authCheck = null;
109
+ let authVerified = false;
110
+ ['manifest:auth:login', 'manifest:auth:logout', 'manifest:auth:anonymous', 'manifest:auth:session-cleared', 'manifest:auth:initialized']
111
+ .forEach(type => window.addEventListener(type, () => { authCheck = null; authVerified = false; }));
112
+
113
+ async function verifyAuthenticated() {
114
+ if (!window.ManifestAppwriteAuthConfig || authVerified) return;
115
+ if (!authCheck) {
116
+ authCheck = (async () => {
117
+ const { client } = await window.ManifestAppwriteAuthConfig.getAppwriteClient();
118
+ if (!client || !window.Appwrite?.Account) return true;
119
+ try {
120
+ await new window.Appwrite.Account(client).get();
121
+ } catch (error) {
122
+ if (error?.code === 401) return false;
123
+ }
124
+ authVerified = true; // Anonymous / API-key access proceeds too
125
+ return true;
126
+ })().catch(() => true).finally(() => { authCheck = null; });
127
+ }
128
+ if (!(await authCheck)) {
129
+ throw new Error('[Manifest Data] User not authenticated. Please sign in to Appwrite first.');
130
+ }
131
+ }
132
+
107
133
  // Load rows from Appwrite table
108
134
  async function loadTableRows(databaseId, tableId, queries = []) {
109
135
  const services = await getAppwriteDataServices();
@@ -111,29 +137,7 @@ async function loadTableRows(databaseId, tableId, queries = []) {
111
137
  throw new Error('[Manifest Data] Appwrite TablesDB service not available');
112
138
  }
113
139
 
114
- // Optional: Verify authentication if auth plugin is available
115
- // Skip if using API key or anonymous access
116
- if (window.ManifestAppwriteAuthConfig) {
117
- try {
118
- const { client } = await window.ManifestAppwriteAuthConfig.getAppwriteClient();
119
- if (client && window.Appwrite?.Account) {
120
- const account = new window.Appwrite.Account(client);
121
- try {
122
- await account.get(); // This will throw if not authenticated
123
- } catch (authError) {
124
- // Only throw if auth plugin is being used - otherwise allow anonymous/API key access
125
- if (authError.code === 401) {
126
- throw new Error('[Manifest Data] User not authenticated. Please sign in to Appwrite first.');
127
- }
128
- }
129
- }
130
- } catch (authCheckError) {
131
- // If auth check fails, still try the request (might be anonymous access or API key)
132
- if (authCheckError.message?.includes('not authenticated')) {
133
- throw authCheckError;
134
- }
135
- }
136
- }
140
+ await verifyAuthenticated();
137
141
 
138
142
  try {
139
143
  const response = await services.tablesDB.listRows({
@@ -410,6 +410,7 @@ function initializeComboboxPlugin() {
410
410
  generatedMenu = menu; // tracked so cleanup() can remove it on re-render
411
411
  if (src) src.style.setProperty('display', 'none', 'important');
412
412
  menu.setAttribute('popover', 'manual');
413
+ menu.setAttribute('x-defer.off', ''); // options are built here, not by directives
413
414
  menu.id = 'combobox-menu-' + rand();
414
415
  menu.setAttribute('role', 'listbox');
415
416
  if (multiple) menu.setAttribute('aria-multiselectable', 'true');
@@ -0,0 +1,52 @@
1
+ /* Manifest Computed */
2
+
3
+ (function () {
4
+
5
+ // $computed(fn): cached derivation — recomputed once per flush when a tracked dependency changes
6
+ function computed(fn) {
7
+ if (typeof fn !== 'function') throw new TypeError('[Manifest Computed] $computed expects a function');
8
+ return Alpine.interceptor(() => {}, obj => {
9
+ obj.initialize = (data, path, key) => install(data, key, fn);
10
+ })(fn);
11
+ }
12
+
13
+ function install(data, key, fn) {
14
+ let value;
15
+ let failed = false;
16
+ let first = true;
17
+
18
+ const compute = () => {
19
+ try {
20
+ value = fn.call(data);
21
+ failed = false;
22
+ } catch (error) {
23
+ // Keep the last good value; report once per failure streak
24
+ if (!failed) console.warn(`[Manifest Computed] "${key}" threw — keeping its last value`, error);
25
+ failed = true;
26
+ }
27
+ return value;
28
+ };
29
+
30
+ const effect = Alpine.effect(() => {
31
+ const next = compute();
32
+ if (first) { first = false; return; }
33
+ data[key] = next;
34
+ });
35
+
36
+ // Release with the owning element (stores have no $el and live for the page)
37
+ const el = data.$el;
38
+ if (el) Alpine.onElRemoved(el, () => Alpine.release(effect));
39
+
40
+ return value;
41
+ }
42
+
43
+ function initializeComputedPlugin() {
44
+ Alpine.magic('computed', () => computed);
45
+ }
46
+
47
+ window.$computed = computed;
48
+
49
+ document.addEventListener('alpine:init', initializeComputedPlugin);
50
+ if (window.Alpine && typeof window.Alpine.magic === 'function') initializeComputedPlugin();
51
+
52
+ })();
package/lib/manifest.d.ts CHANGED
@@ -412,7 +412,7 @@ export interface ManifestTextEdit {
412
412
  export type ManifestDirective =
413
413
  | 'x-route' | 'x-anchors'
414
414
  | 'x-icon' | 'x-svg' | 'x-markdown' | 'x-code' | 'x-code-group'
415
- | 'x-toast' | 'x-tooltip' | 'x-carousel' | 'x-virtual' | 'x-resize'
415
+ | 'x-toast' | 'x-tooltip' | 'x-carousel' | 'x-virtual' | 'x-resize' | 'x-defer'
416
416
  | 'x-chart' | 'x-date' | 'x-color' | 'x-colorpicker' | 'x-combobox'
417
417
  | 'x-export' | 'x-pay' | 'x-files' | 'x-data-files'
418
418
  | 'x-edit' | 'x-text-edit';
@@ -430,6 +430,11 @@ declare global {
430
430
  const $modify: ManifestModify;
431
431
  /** Run a callback safely, returning undefined on error. */
432
432
  const $try: ManifestTry;
433
+ /**
434
+ * Cached derivation for `x-data` / `Alpine.data` scopes: `rows: $computed(function () { return this.items.filter(…) })`.
435
+ * Recomputed once per flush when a tracked dependency changes; reads as a plain property with a stable identity in between.
436
+ */
437
+ const $computed: <T>(fn: (this: any) => T) => T;
433
438
  /** Appwrite auth plugin (present when the plugin is loaded). */
434
439
  const $auth: ManifestAuth;
435
440
  /** Localization plugin. */
@@ -485,6 +490,8 @@ declare global {
485
490
  loadPlugin(name: string, version?: string): Promise<unknown>;
486
491
  loadTailwind(version?: string): Promise<unknown>;
487
492
  getPluginUrl(name: string, version?: string): string;
493
+ /** Defer a closed container built at runtime (call before Alpine.initTree). */
494
+ defer?(el: Element): unknown;
488
495
  };
489
496
  Alpine?: unknown;
490
497
  __manifestLoaded?: unknown;