haori 0.4.8 → 0.4.9

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/dist/index.d.ts CHANGED
@@ -1002,6 +1002,6 @@ declare class TextFragment extends Fragment {
1002
1002
  evaluate(): Promise<void>;
1003
1003
  }
1004
1004
 
1005
- export declare const version = "0.4.8";
1005
+ export declare const version = "0.4.9";
1006
1006
 
1007
1007
  export { }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haori",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "description": "A lightweight HTML-first UI engine powered by declarative data bindings and expressions.",
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -14,5 +14,5 @@ import './observer';
14
14
  export { Core, Env, Fragment, Form, Haori, Log, Queue };
15
15
  export type { HaoriRuntime } from './env';
16
16
  export default Haori;
17
- export declare const version = "0.4.8";
17
+ export declare const version = "0.4.9";
18
18
  //# sourceMappingURL=index.d.ts.map
package/dist/src/index.js CHANGED
@@ -17,5 +17,5 @@ export { Core, Env, Fragment, Form, Haori, Log, Queue };
17
17
  // デフォルトエクスポート(Haoriをメインとして提供)
18
18
  export default Haori;
19
19
  // バージョン情報
20
- export const version = '0.4.8';
20
+ export const version = '0.4.9';
21
21
  //# sourceMappingURL=index.js.map
@@ -107,6 +107,10 @@ export default class Procedure {
107
107
  private static readonly DATA_PLACEHOLDER_REGEX;
108
108
  /** 属性全体が単一テンプレート式かを判定する正規表現 */
109
109
  private static readonly SINGLE_PLACEHOLDER_REGEX;
110
+ /** click 手続きの再入を防ぐ対象要素の集合 */
111
+ private static readonly RUNNING_CLICK_TARGETS;
112
+ /** この Procedure が扱うイベント種別 */
113
+ private readonly eventType;
110
114
  /**
111
115
  * イベント属性名を正しく生成します。
112
116
  * 例: ("click", "fetch") => "data-click-fetch"
@@ -209,6 +213,19 @@ export default class Procedure {
209
213
  * @returns 実行成功時は true、停止や失敗時は false
210
214
  */
211
215
  private execute;
216
+ /**
217
+ * click 手続きの重複実行を防ぐためのロックを取得します。
218
+ *
219
+ * @returns ロック情報。取得不要なら null、取得失敗なら false。
220
+ */
221
+ private acquireExecutionLock;
222
+ /**
223
+ * 取得済みの実行ロックを解放します。
224
+ *
225
+ * @param executionLock 解放対象のロック情報。
226
+ * @returns 戻り値はありません。
227
+ */
228
+ private releaseExecutionLock;
212
229
  /**
213
230
  * フェッチ後の処理を実行します。
214
231
  */
@@ -1 +1 @@
1
- {"version":3,"file":"procedure.d.ts","sourceRoot":"","sources":["../../src/procedure.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"procedure.d.ts","sourceRoot":"","sources":["../../src/procedure.ts"],"names":[],"mappings":"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"}
@@ -676,9 +676,11 @@ ${body}
676
676
  constructor(arg1, arg2 = null) {
677
677
  if (Procedure.isElementFragment(arg1)) {
678
678
  this.options = Procedure.buildOptions(arg1, arg2);
679
+ this.eventType = arg2;
679
680
  }
680
681
  else {
681
682
  this.options = arg1;
683
+ this.eventType = null;
682
684
  }
683
685
  }
684
686
  /**
@@ -703,164 +705,209 @@ ${body}
703
705
  * @returns 実行成功時は true、停止や失敗時は false
704
706
  */
705
707
  async execute() {
706
- if (Object.keys(this.options).length === 0) {
708
+ const executionLock = this.acquireExecutionLock();
709
+ if (executionLock === false) {
707
710
  return false;
708
711
  }
709
- if (this.options.formFragment &&
710
- this.validate(this.options.formFragment) === false) {
711
- return false;
712
- }
713
- const confirmed = await this.confirm();
714
- if (!confirmed) {
715
- return false;
716
- }
717
- let fetchUrl = this.options.fetchUrl;
718
- let fetchOptions = this.options.fetchOptions;
719
- if (this.options.beforeCallback) {
720
- const result = this.options.beforeCallback(fetchUrl || null, fetchOptions || null);
721
- if (result !== undefined && result !== null) {
722
- if (result === false || (typeof result === 'object' && result.stop)) {
723
- return false;
724
- }
725
- if (typeof result === 'object') {
726
- fetchUrl = ('fetchUrl' in result ? result.fetchUrl : fetchUrl);
727
- fetchOptions = ('fetchOptions' in result ? result.fetchOptions : fetchOptions);
728
- }
712
+ try {
713
+ if (Object.keys(this.options).length === 0) {
714
+ return false;
729
715
  }
730
- }
731
- // フォーム値と data を統合してペイロードを作成
732
- const payload = {};
733
- if (this.options.formFragment) {
734
- const formValues = Form.getValues(this.options.formFragment);
735
- Object.assign(payload, formValues);
736
- }
737
- if (this.options.data && typeof this.options.data === 'object') {
738
- Object.assign(payload, this.options.data);
739
- }
740
- const hasPayload = Object.keys(payload).length > 0;
741
- if (fetchUrl) {
742
- const finalOptions = { ...(fetchOptions || {}) };
743
- const headers = new Headers(finalOptions.headers || undefined);
744
- const requestedMethod = (finalOptions.method || 'GET').toUpperCase();
745
- const isDemoQueryNormalization = Env.runtime === 'demo' && !isQueryTransportMethod(requestedMethod);
746
- const method = isDemoQueryNormalization ? 'GET' : requestedMethod;
747
- finalOptions.method = method;
748
- if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {
749
- if (hasPayload) {
750
- fetchUrl = appendPayloadToUrl(fetchUrl, payload);
716
+ if (this.options.formFragment &&
717
+ this.validate(this.options.formFragment) === false) {
718
+ return false;
719
+ }
720
+ const confirmed = await this.confirm();
721
+ if (!confirmed) {
722
+ return false;
723
+ }
724
+ let fetchUrl = this.options.fetchUrl;
725
+ let fetchOptions = this.options.fetchOptions;
726
+ if (this.options.beforeCallback) {
727
+ const result = this.options.beforeCallback(fetchUrl || null, fetchOptions || null);
728
+ if (result !== undefined && result !== null) {
729
+ if (result === false || (typeof result === 'object' && result.stop)) {
730
+ return false;
731
+ }
732
+ if (typeof result === 'object') {
733
+ fetchUrl = ('fetchUrl' in result ? result.fetchUrl : fetchUrl);
734
+ fetchOptions = ('fetchOptions' in result ? result.fetchOptions : fetchOptions);
735
+ }
751
736
  }
752
737
  }
753
- else if (hasPayload) {
754
- const contentType = headers.get('Content-Type') || '';
755
- if (/multipart\/form-data/i.test(contentType)) {
756
- headers.delete('Content-Type');
757
- const formData = new FormData();
758
- for (const [k, v] of Object.entries(payload)) {
759
- if (v === undefined || v === null) {
760
- formData.append(k, '');
761
- }
762
- else if (v instanceof Blob) {
763
- formData.append(k, v);
764
- }
765
- else if (Array.isArray(v)) {
766
- v.forEach(item => formData.append(k, String(item)));
767
- }
768
- else if (typeof v === 'object') {
769
- formData.append(k, JSON.stringify(v));
770
- }
771
- else {
772
- formData.append(k, String(v));
773
- }
738
+ // フォーム値と data を統合してペイロードを作成
739
+ const payload = {};
740
+ if (this.options.formFragment) {
741
+ const formValues = Form.getValues(this.options.formFragment);
742
+ Object.assign(payload, formValues);
743
+ }
744
+ if (this.options.data && typeof this.options.data === 'object') {
745
+ Object.assign(payload, this.options.data);
746
+ }
747
+ const hasPayload = Object.keys(payload).length > 0;
748
+ if (fetchUrl) {
749
+ const finalOptions = { ...(fetchOptions || {}) };
750
+ const headers = new Headers(finalOptions.headers || undefined);
751
+ const requestedMethod = (finalOptions.method || 'GET').toUpperCase();
752
+ const isDemoQueryNormalization = Env.runtime === 'demo' && !isQueryTransportMethod(requestedMethod);
753
+ const method = isDemoQueryNormalization ? 'GET' : requestedMethod;
754
+ finalOptions.method = method;
755
+ if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {
756
+ if (hasPayload) {
757
+ fetchUrl = appendPayloadToUrl(fetchUrl, payload);
774
758
  }
775
- finalOptions.body = formData;
776
759
  }
777
- else if (/application\/x-www-form-urlencoded/i.test(contentType)) {
778
- const params = new URLSearchParams();
779
- for (const [k, v] of Object.entries(payload)) {
780
- if (v === undefined) {
781
- continue;
782
- }
783
- if (v === null) {
784
- params.append(k, '');
785
- }
786
- else if (Array.isArray(v)) {
787
- v.forEach(item => params.append(k, String(item)));
788
- }
789
- else if (typeof v === 'object') {
790
- params.append(k, JSON.stringify(v));
760
+ else if (hasPayload) {
761
+ const contentType = headers.get('Content-Type') || '';
762
+ if (/multipart\/form-data/i.test(contentType)) {
763
+ headers.delete('Content-Type');
764
+ const formData = new FormData();
765
+ for (const [k, v] of Object.entries(payload)) {
766
+ if (v === undefined || v === null) {
767
+ formData.append(k, '');
768
+ }
769
+ else if (v instanceof Blob) {
770
+ formData.append(k, v);
771
+ }
772
+ else if (Array.isArray(v)) {
773
+ v.forEach(item => formData.append(k, String(item)));
774
+ }
775
+ else if (typeof v === 'object') {
776
+ formData.append(k, JSON.stringify(v));
777
+ }
778
+ else {
779
+ formData.append(k, String(v));
780
+ }
791
781
  }
792
- else {
793
- params.append(k, String(v));
782
+ finalOptions.body = formData;
783
+ }
784
+ else if (/application\/x-www-form-urlencoded/i.test(contentType)) {
785
+ const params = new URLSearchParams();
786
+ for (const [k, v] of Object.entries(payload)) {
787
+ if (v === undefined) {
788
+ continue;
789
+ }
790
+ if (v === null) {
791
+ params.append(k, '');
792
+ }
793
+ else if (Array.isArray(v)) {
794
+ v.forEach(item => params.append(k, String(item)));
795
+ }
796
+ else if (typeof v === 'object') {
797
+ params.append(k, JSON.stringify(v));
798
+ }
799
+ else {
800
+ params.append(k, String(v));
801
+ }
794
802
  }
803
+ finalOptions.body = params;
804
+ }
805
+ else {
806
+ headers.set('Content-Type', 'application/json');
807
+ finalOptions.body = JSON.stringify(payload);
795
808
  }
796
- finalOptions.body = params;
797
809
  }
798
- else {
799
- headers.set('Content-Type', 'application/json');
800
- finalOptions.body = JSON.stringify(payload);
810
+ finalOptions.headers = headers;
811
+ let queryString;
812
+ if (isDemoQueryNormalization) {
813
+ queryString = fetchUrl
814
+ ? new URL(fetchUrl, window.location.href).search || undefined
815
+ : undefined;
816
+ headers.delete('Content-Type');
817
+ Log.info('Haori demo fetch normalization', {
818
+ runtime: Env.runtime,
819
+ requestedMethod,
820
+ effectiveMethod: method,
821
+ transportMode: 'query-get',
822
+ url: fetchUrl,
823
+ payload: hasPayload ? payload : undefined,
824
+ queryString,
825
+ });
801
826
  }
802
- }
803
- finalOptions.headers = headers;
804
- let queryString;
805
- if (isDemoQueryNormalization) {
806
- queryString = fetchUrl
807
- ? new URL(fetchUrl, window.location.href).search || undefined
808
- : undefined;
809
- headers.delete('Content-Type');
810
- Log.info('Haori demo fetch normalization', {
811
- runtime: Env.runtime,
812
- requestedMethod,
813
- effectiveMethod: method,
814
- transportMode: 'query-get',
815
- url: fetchUrl,
816
- payload: hasPayload ? payload : undefined,
817
- queryString,
818
- });
819
- }
820
- // fetchstartイベントを発火
821
- if (this.options.targetFragment && fetchUrl) {
822
- const startedAt = performance.now();
823
- const fetchStartMetadata = {
824
- runtime: Env.runtime,
825
- requestedMethod,
826
- effectiveMethod: method,
827
- transportMode: isDemoQueryNormalization ? 'query-get' : 'http',
828
- ...(isDemoQueryNormalization ? { queryString } : {}),
829
- };
830
- HaoriEvent.fetchStart(this.options.targetFragment.getTarget(), fetchUrl, finalOptions, hasPayload ? payload : undefined, fetchStartMetadata);
831
- return fetch(fetchUrl, finalOptions)
832
- .then(response => {
833
- return this.handleFetchResult(response, fetchUrl || undefined, startedAt);
834
- })
835
- .catch(error => {
836
- if (fetchUrl) {
837
- HaoriEvent.fetchError(this.options.targetFragment.getTarget(), fetchUrl, error);
838
- }
839
- throw error;
827
+ // fetchstartイベントを発火
828
+ if (this.options.targetFragment && fetchUrl) {
829
+ const startedAt = performance.now();
830
+ const fetchStartMetadata = {
831
+ runtime: Env.runtime,
832
+ requestedMethod,
833
+ effectiveMethod: method,
834
+ transportMode: isDemoQueryNormalization ? 'query-get' : 'http',
835
+ ...(isDemoQueryNormalization ? { queryString } : {}),
836
+ };
837
+ HaoriEvent.fetchStart(this.options.targetFragment.getTarget(), fetchUrl, finalOptions, hasPayload ? payload : undefined, fetchStartMetadata);
838
+ return fetch(fetchUrl, finalOptions)
839
+ .then(response => {
840
+ return this.handleFetchResult(response, fetchUrl || undefined, startedAt);
841
+ })
842
+ .catch(error => {
843
+ if (fetchUrl) {
844
+ HaoriEvent.fetchError(this.options.targetFragment.getTarget(), fetchUrl, error);
845
+ }
846
+ throw error;
847
+ });
848
+ }
849
+ return fetch(fetchUrl, finalOptions).then(response => {
850
+ return this.handleFetchResult(response, fetchUrl || undefined);
840
851
  });
841
852
  }
842
- return fetch(fetchUrl, finalOptions).then(response => {
843
- return this.handleFetchResult(response, fetchUrl || undefined);
853
+ // fetchUrlが無い場合(changeイベント等)、bindFragmentsが無ければformFragmentにバインド
854
+ if ((!this.options.bindFragments ||
855
+ this.options.bindFragments.length === 0) &&
856
+ this.options.formFragment &&
857
+ hasPayload) {
858
+ // 双方向バインディング: フォーム値を自動的にバインディングデータに反映
859
+ const formFragment = this.options.formFragment;
860
+ const formElement = formFragment.getTarget();
861
+ formElement.setAttribute(`${Env.prefix}bind`, JSON.stringify(payload));
862
+ const bindingData = formFragment.getBindingData();
863
+ Object.assign(bindingData, payload);
864
+ await Core.setBindingData(formElement, bindingData);
865
+ }
866
+ const merged = hasPayload ? payload : {};
867
+ const response = new Response(JSON.stringify(merged), {
868
+ headers: { 'Content-Type': 'application/json' },
844
869
  });
870
+ return this.handleFetchResult(response);
871
+ }
872
+ finally {
873
+ this.releaseExecutionLock(executionLock);
874
+ }
875
+ }
876
+ /**
877
+ * click 手続きの重複実行を防ぐためのロックを取得します。
878
+ *
879
+ * @returns ロック情報。取得不要なら null、取得失敗なら false。
880
+ */
881
+ acquireExecutionLock() {
882
+ if (this.eventType !== 'click' || !this.options.targetFragment) {
883
+ return null;
884
+ }
885
+ const target = this.options.targetFragment.getTarget();
886
+ if (Procedure.RUNNING_CLICK_TARGETS.has(target) ||
887
+ target.hasAttribute('disabled')) {
888
+ return false;
889
+ }
890
+ Procedure.RUNNING_CLICK_TARGETS.add(target);
891
+ target.setAttribute('disabled', '');
892
+ return {
893
+ target,
894
+ appliedDisabledAttribute: true,
895
+ };
896
+ }
897
+ /**
898
+ * 取得済みの実行ロックを解放します。
899
+ *
900
+ * @param executionLock 解放対象のロック情報。
901
+ * @returns 戻り値はありません。
902
+ */
903
+ releaseExecutionLock(executionLock) {
904
+ if (!executionLock) {
905
+ return;
906
+ }
907
+ Procedure.RUNNING_CLICK_TARGETS.delete(executionLock.target);
908
+ if (executionLock.appliedDisabledAttribute) {
909
+ executionLock.target.removeAttribute('disabled');
845
910
  }
846
- // fetchUrlが無い場合(changeイベント等)、bindFragmentsが無ければformFragmentにバインド
847
- if ((!this.options.bindFragments ||
848
- this.options.bindFragments.length === 0) &&
849
- this.options.formFragment &&
850
- hasPayload) {
851
- // 双方向バインディング: フォーム値を自動的にバインディングデータに反映
852
- const formFragment = this.options.formFragment;
853
- const formElement = formFragment.getTarget();
854
- formElement.setAttribute(`${Env.prefix}bind`, JSON.stringify(payload));
855
- const bindingData = formFragment.getBindingData();
856
- Object.assign(bindingData, payload);
857
- await Core.setBindingData(formElement, bindingData);
858
- }
859
- const merged = hasPayload ? payload : {};
860
- const response = new Response(JSON.stringify(merged), {
861
- headers: { 'Content-Type': 'application/json' },
862
- });
863
- return this.handleFetchResult(response);
864
911
  }
865
912
  /**
866
913
  * フェッチ後の処理を実行します。
@@ -1475,5 +1522,7 @@ ${body}
1475
1522
  Procedure.DATA_PLACEHOLDER_REGEX = /\{\{\{([\s\S]+?)\}\}\}|\{\{([\s\S]+?)\}\}/g;
1476
1523
  /** 属性全体が単一テンプレート式かを判定する正規表現 */
1477
1524
  Procedure.SINGLE_PLACEHOLDER_REGEX = /^(\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\})$/;
1525
+ /** click 手続きの再入を防ぐ対象要素の集合 */
1526
+ Procedure.RUNNING_CLICK_TARGETS = new WeakSet();
1478
1527
  export default Procedure;
1479
1528
  //# sourceMappingURL=procedure.js.map