cotomy 0.1.66 → 0.1.67
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 +7 -0
- package/dist/browser/cotomy.js +25 -56
- package/dist/browser/cotomy.js.map +1 -1
- package/dist/browser/cotomy.min.js +1 -1
- package/dist/browser/cotomy.min.js.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/form.js +26 -57
- package/dist/esm/form.js.map +1 -1
- package/dist/types/form.d.ts +4 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,13 @@ npm i cotomy
|
|
|
20
20
|
Cotomy will continue to expand with more detailed usage instructions and code examples added to the README in the future.
|
|
21
21
|
For the latest updates, please check the official documentation or repository regularly.
|
|
22
22
|
|
|
23
|
+
### Entity API forms
|
|
24
|
+
|
|
25
|
+
`CotomyEntityApiForm` targets REST endpoints that identify records with a single surrogate key.
|
|
26
|
+
Attach `data-cotomy-entity-key="<id>"` to the form when editing an existing entity; omit the attribute (or leave it empty) to issue a `POST` to the base `action` URL.
|
|
27
|
+
On `201 Created`, the form reads the `Location` header and stores the generated key back into `data-cotomy-entity-key`, enabling subsequent `PUT` submissions.
|
|
28
|
+
Composite or natural keys are no longer supported—migrate any legacy markup that relied on `data-cotomy-keyindex` or multiple key inputs to the new surrogate-key flow.
|
|
29
|
+
|
|
23
30
|
The core of Cotomy is `CotomyElement`, which is constructed as a wrapper for `Element`.
|
|
24
31
|
By passing HTML and CSS strings to the constructor, it is possible to generate Element designs with a limited scope.
|
|
25
32
|
|
package/dist/browser/cotomy.js
CHANGED
|
@@ -2524,31 +2524,34 @@ class CotomyApiForm extends CotomyForm {
|
|
|
2524
2524
|
}
|
|
2525
2525
|
}
|
|
2526
2526
|
class CotomyEntityApiForm extends CotomyApiForm {
|
|
2527
|
+
get entityKey() {
|
|
2528
|
+
return this.attribute("data-cotomy-entity-key") ?? undefined;
|
|
2529
|
+
}
|
|
2530
|
+
get requiresEntityKey() {
|
|
2531
|
+
return this.attribute("data-cotomy-identify") !== "false";
|
|
2532
|
+
}
|
|
2533
|
+
get hasEntityKey() {
|
|
2534
|
+
return !!this.entityKey;
|
|
2535
|
+
}
|
|
2527
2536
|
actionUrl() {
|
|
2528
|
-
const
|
|
2529
|
-
const
|
|
2530
|
-
|
|
2537
|
+
const action = this.attribute("action");
|
|
2538
|
+
const normalized = action.replace(/\/+$/, "");
|
|
2539
|
+
if (!this.entityKey) {
|
|
2540
|
+
return action.endsWith("/") ? action : `${normalized}/`;
|
|
2541
|
+
}
|
|
2542
|
+
return `${normalized}/${encodeURIComponent(this.entityKey)}`;
|
|
2531
2543
|
}
|
|
2532
2544
|
method() {
|
|
2533
2545
|
if (this.hasAttribute("method") && this.attribute("method") !== "") {
|
|
2534
2546
|
return this.attribute("method");
|
|
2535
2547
|
}
|
|
2536
|
-
|
|
2537
|
-
return "put";
|
|
2538
|
-
if (this.pathKeyInputs.length > 0 && this.pathKeyInputs.every(e => e.readonly))
|
|
2539
|
-
return "put";
|
|
2540
|
-
if (this.keyInputs.length > 0 && this.keyInputs.every(e => e.readonly))
|
|
2541
|
-
return "put";
|
|
2542
|
-
return "post";
|
|
2548
|
+
return this.hasEntityKey ? "put" : "post";
|
|
2543
2549
|
}
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
setExternalKey(response) {
|
|
2548
|
-
if (this.requiresExternalKey && response.status === StatusCodes.CREATED) {
|
|
2549
|
-
if (this.hasExternalKey) {
|
|
2550
|
+
setEntityKey(response) {
|
|
2551
|
+
if (this.requiresEntityKey && response.status === StatusCodes.CREATED) {
|
|
2552
|
+
if (this.hasEntityKey) {
|
|
2550
2553
|
if (CotomyDebugSettings.isEnabled(CotomyDebugFeature.FormLoad)) {
|
|
2551
|
-
console.warn("
|
|
2554
|
+
console.warn("Entity key already exists, but server responded with 201 Created. Possible duplicate POST.");
|
|
2552
2555
|
}
|
|
2553
2556
|
return;
|
|
2554
2557
|
}
|
|
@@ -2577,49 +2580,19 @@ class CotomyEntityApiForm extends CotomyApiForm {
|
|
|
2577
2580
|
}
|
|
2578
2581
|
const addedParts = locationParts.slice(actionParts.length).filter(Boolean);
|
|
2579
2582
|
if (addedParts.length === 1 && addedParts[0]) {
|
|
2580
|
-
this.attribute("data-cotomy-key", addedParts[0]);
|
|
2583
|
+
this.attribute("data-cotomy-entity-key", addedParts[0]);
|
|
2581
2584
|
}
|
|
2582
2585
|
else {
|
|
2583
|
-
const msg = `Location does not contain a single
|
|
2586
|
+
const msg = `Location does not contain a single entity key segment.
|
|
2584
2587
|
action="${baseAction}", location="${locPath}", added=["${addedParts.join('","')}"]`;
|
|
2585
2588
|
throw new Error(msg);
|
|
2586
2589
|
}
|
|
2587
2590
|
}
|
|
2588
2591
|
}
|
|
2589
|
-
get requiresExternalKey() {
|
|
2590
|
-
return this.attribute("data-cotomy-identify") !== "false" && this.keyInputs.length == 0 && this.pathKeyInputs.length == 0;
|
|
2591
|
-
}
|
|
2592
|
-
get hasExternalKey() {
|
|
2593
|
-
return !!this.externalKey;
|
|
2594
|
-
}
|
|
2595
|
-
get pathKeyInputs() {
|
|
2596
|
-
return this.find("[data-cotomy-keyindex]").sort((a, b) => {
|
|
2597
|
-
const aIndex = parseInt(a.attribute("data-cotomy-keyindex") ?? "0");
|
|
2598
|
-
const bIndex = parseInt(b.attribute("data-cotomy-keyindex") ?? "0");
|
|
2599
|
-
return aIndex - bIndex;
|
|
2600
|
-
});
|
|
2601
|
-
}
|
|
2602
|
-
get keyInputs() {
|
|
2603
|
-
return this.contains("[data-cotomy-key]") ? this.find("[data-cotomy-key]").sort((a, b) => {
|
|
2604
|
-
const aIndex = parseInt(a.attribute("data-cotomy-key") ?? "0");
|
|
2605
|
-
const bIndex = parseInt(b.attribute("data-cotomy-key") ?? "0");
|
|
2606
|
-
return aIndex - bIndex;
|
|
2607
|
-
}) : this.find("[name][data-cotomy-key]");
|
|
2608
|
-
}
|
|
2609
|
-
get usePathKey() {
|
|
2610
|
-
const use = this.pathKeyInputs.length > 0 || this.requiresExternalKey;
|
|
2611
|
-
if (use && this.hasExternalKey && CotomyDebugSettings.isEnabled(CotomyDebugFeature.FormLoad)) {
|
|
2612
|
-
console.warn("Both externalKey and pathKeyInputs are present. Using externalKey and ignoring pathKeyInputs.");
|
|
2613
|
-
}
|
|
2614
|
-
return use;
|
|
2615
|
-
}
|
|
2616
|
-
get pathKeyString() {
|
|
2617
|
-
return this.externalKey || this.pathKeyInputs.map(e => e.value).join("/");
|
|
2618
|
-
}
|
|
2619
2592
|
async submitToApiAsync(formData) {
|
|
2620
2593
|
const response = await super.submitToApiAsync(formData);
|
|
2621
|
-
if (this.
|
|
2622
|
-
this.
|
|
2594
|
+
if (this.requiresEntityKey && response.status === StatusCodes.CREATED) {
|
|
2595
|
+
this.setEntityKey(response);
|
|
2623
2596
|
}
|
|
2624
2597
|
return response;
|
|
2625
2598
|
}
|
|
@@ -2684,9 +2657,7 @@ class CotomyEntityFillApiForm extends CotomyEntityApiForm {
|
|
|
2684
2657
|
return new CotomyViewRenderer(this, this.bindNameGenerator());
|
|
2685
2658
|
}
|
|
2686
2659
|
async loadAsync() {
|
|
2687
|
-
|
|
2688
|
-
const hasKeyInputs = this.keyInputs.length > 0 && this.keyInputs.every(e => !!e.value);
|
|
2689
|
-
if (!this.hasExternalKey && !hasPathKeys && !hasKeyInputs) {
|
|
2660
|
+
if (!this.hasEntityKey) {
|
|
2690
2661
|
return new CotomyApiResponse();
|
|
2691
2662
|
}
|
|
2692
2663
|
const api = this.apiClient();
|
|
@@ -2738,8 +2709,6 @@ class CotomyEntityFillApiForm extends CotomyEntityApiForm {
|
|
|
2738
2709
|
await this.fillObjectAsync(this.bindNameGenerator(), await response.objectAsync());
|
|
2739
2710
|
await this.renderer().applyAsync(response);
|
|
2740
2711
|
}
|
|
2741
|
-
this.pathKeyInputs.forEach(e => e.readonly = true);
|
|
2742
|
-
this.keyInputs.forEach(e => e.readonly = true);
|
|
2743
2712
|
this.find("textarea").forEach(e => e.input());
|
|
2744
2713
|
}
|
|
2745
2714
|
}
|