refactor
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const db = require("../db/polygons");
|
const db = require("../db/polygons");
|
||||||
|
const { normalizeEntityContract } = require("../types/contracts");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ router.get("/", (req, res) => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const rows = db.prepare(sql).all(...params);
|
const rows = db.prepare(sql).all(...params);
|
||||||
res.json(rows.map(normalizeEntityRow));
|
res.json(rows.map(normalizeEntityContract));
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/search", (req, res) => {
|
router.get("/search", (req, res) => {
|
||||||
@@ -55,7 +56,7 @@ router.get("/search", (req, res) => {
|
|||||||
LIMIT ?
|
LIMIT ?
|
||||||
`).all(pattern, limit);
|
`).all(pattern, limit);
|
||||||
|
|
||||||
res.json(rows.map(normalizeEntityRow));
|
res.json(rows.map(normalizeEntityContract));
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/:id", (req, res) => {
|
router.get("/:id", (req, res) => {
|
||||||
@@ -75,25 +76,11 @@ router.get("/:id", (req, res) => {
|
|||||||
return res.status(404).json({ error: "Entity not found" });
|
return res.status(404).json({ error: "Entity not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(normalizeEntityRow(row));
|
res.json(normalizeEntityContract(row));
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
function normalizeEntityRow(row) {
|
|
||||||
return {
|
|
||||||
id: row.id,
|
|
||||||
name: row.name,
|
|
||||||
slug: row.slug,
|
|
||||||
description: row.description,
|
|
||||||
type_id: row.type_id,
|
|
||||||
status: row.status,
|
|
||||||
created_at: row.created_at,
|
|
||||||
updated_at: row.updated_at,
|
|
||||||
geometry_count: Number(row.geometry_count || 0),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeLimit(value, fallback = 25, max = 100) {
|
function normalizeLimit(value, fallback = 25, max = 100) {
|
||||||
const num = Number(value);
|
const num = Number(value);
|
||||||
if (!Number.isFinite(num)) return fallback;
|
if (!Number.isFinite(num)) return fallback;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const db = require("../db/polygons");
|
const db = require("../db/polygons");
|
||||||
|
const { normalizeFeatureCollectionContract, normalizeFeatureContract } = require("../types/contracts");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -70,13 +71,13 @@ router.get("/", (req, res) => {
|
|||||||
const geometryIds = rows.map((row) => String(row.id));
|
const geometryIds = rows.map((row) => String(row.id));
|
||||||
const linksByGeometryId = loadGeometryLinksByGeometryId(geometryIds);
|
const linksByGeometryId = loadGeometryLinksByGeometryId(geometryIds);
|
||||||
|
|
||||||
res.json({
|
res.json(normalizeFeatureCollectionContract({
|
||||||
type: "FeatureCollection",
|
type: "FeatureCollection",
|
||||||
features: rows.map((row) => {
|
features: rows.map((row) => {
|
||||||
const linkedEntities = linksByGeometryId.get(String(row.id)) || [];
|
const linkedEntities = linksByGeometryId.get(String(row.id)) || [];
|
||||||
return buildFeatureFromRow(row, linkedEntities);
|
return buildFeatureFromRow(row, linkedEntities);
|
||||||
}),
|
}),
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -95,7 +96,7 @@ function buildFeatureFromRow(row, linkedEntities = []) {
|
|||||||
? storedGeometryType
|
? storedGeometryType
|
||||||
: normalizeGeometryType(primaryEntity?.entity_type_id);
|
: normalizeGeometryType(primaryEntity?.entity_type_id);
|
||||||
|
|
||||||
return {
|
return normalizeFeatureContract({
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties: {
|
properties: {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
@@ -112,7 +113,7 @@ function buildFeatureFromRow(row, linkedEntities = []) {
|
|||||||
entity_type_id: primaryEntity?.entity_type_id || null,
|
entity_type_id: primaryEntity?.entity_type_id || null,
|
||||||
},
|
},
|
||||||
geometry: JSON.parse(row.draw_geometry),
|
geometry: JSON.parse(row.draw_geometry),
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeGeometryType(value) {
|
function normalizeGeometryType(value) {
|
||||||
|
|||||||
@@ -2,6 +2,14 @@ const express = require("express");
|
|||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const db = require("../db/polygons");
|
const db = require("../db/polygons");
|
||||||
const { getBBox } = require("../utils/bbox");
|
const { getBBox } = require("../utils/bbox");
|
||||||
|
const {
|
||||||
|
assertEditorSnapshotContract,
|
||||||
|
normalizeEditorSnapshotContract,
|
||||||
|
normalizeSectionCommitContract,
|
||||||
|
normalizeSectionContract,
|
||||||
|
normalizeSectionStateContract,
|
||||||
|
normalizeSectionSubmissionContract,
|
||||||
|
} = require("../types/contracts");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const LOCK_TTL_MS = 15 * 60 * 1000;
|
const LOCK_TTL_MS = 15 * 60 * 1000;
|
||||||
@@ -673,6 +681,8 @@ function applyLinkScopeSnapshot(scope, now) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateSnapshot(snapshot) {
|
function validateSnapshot(snapshot) {
|
||||||
|
assertEditorSnapshotContract(snapshot);
|
||||||
|
|
||||||
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) {
|
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) {
|
||||||
throw createHttpError("snapshot must be an object", 400);
|
throw createHttpError("snapshot must be an object", 400);
|
||||||
}
|
}
|
||||||
@@ -847,71 +857,25 @@ function updateSubmissionReview(submissionId, status, actor, now, reviewNote) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeSectionRow(row) {
|
function normalizeSectionRow(row) {
|
||||||
return {
|
return normalizeSectionContract(row);
|
||||||
id: row.id,
|
|
||||||
title: row.title,
|
|
||||||
description: row.description,
|
|
||||||
user_id: row.user_id || null,
|
|
||||||
created_by: row.created_by,
|
|
||||||
created_at: row.created_at,
|
|
||||||
updated_at: row.updated_at,
|
|
||||||
state: {
|
|
||||||
status: row.status || "editing",
|
|
||||||
head_commit_id: row.head_commit_id || null,
|
|
||||||
version: Number(row.version || 0),
|
|
||||||
locked_by: row.locked_by || null,
|
|
||||||
locked_at: row.locked_at || null,
|
|
||||||
lock_expires_at: row.lock_expires_at || null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeStateRow(row) {
|
function normalizeStateRow(row) {
|
||||||
return {
|
return normalizeSectionStateContract(row);
|
||||||
section_id: row.section_id,
|
|
||||||
status: row.status,
|
|
||||||
head_commit_id: row.head_commit_id || null,
|
|
||||||
version: Number(row.version || 0),
|
|
||||||
locked_by: row.locked_by || null,
|
|
||||||
locked_at: row.locked_at || null,
|
|
||||||
lock_expires_at: row.lock_expires_at || null,
|
|
||||||
updated_at: row.updated_at,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeCommitRow(row, includeSnapshot) {
|
function normalizeCommitRow(row, includeSnapshot) {
|
||||||
const out = {
|
if (includeSnapshot) {
|
||||||
id: row.id,
|
return normalizeSectionCommitContract(row, parseSnapshotJson(row.snapshot_json));
|
||||||
section_id: row.section_id,
|
}
|
||||||
parent_commit_id: row.parent_commit_id || null,
|
return normalizeSectionCommitContract(row);
|
||||||
commit_no: Number(row.commit_no),
|
|
||||||
kind: row.kind,
|
|
||||||
restored_from_commit_id: row.restored_from_commit_id || null,
|
|
||||||
created_by: row.created_by,
|
|
||||||
created_at: row.created_at,
|
|
||||||
title: row.title,
|
|
||||||
note: row.note,
|
|
||||||
snapshot_hash: row.snapshot_hash,
|
|
||||||
};
|
|
||||||
if (includeSnapshot) out.snapshot = parseSnapshotJson(row.snapshot_json);
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeSubmissionRow(row, includeSnapshot) {
|
function normalizeSubmissionRow(row, includeSnapshot) {
|
||||||
const out = {
|
if (includeSnapshot) {
|
||||||
id: row.id,
|
return normalizeSectionSubmissionContract(row, parseSnapshotJson(row.snapshot_json));
|
||||||
section_id: row.section_id,
|
}
|
||||||
commit_id: row.commit_id,
|
return normalizeSectionSubmissionContract(row);
|
||||||
submitted_by: row.submitted_by,
|
|
||||||
submitted_at: row.submitted_at,
|
|
||||||
status: row.status,
|
|
||||||
reviewed_by: row.reviewed_by || null,
|
|
||||||
reviewed_at: row.reviewed_at || null,
|
|
||||||
review_note: row.review_note || null,
|
|
||||||
snapshot_hash: row.snapshot_hash,
|
|
||||||
};
|
|
||||||
if (includeSnapshot) out.snapshot = parseSnapshotJson(row.snapshot_json);
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEmptySnapshot(section) {
|
function buildEmptySnapshot(section) {
|
||||||
@@ -944,7 +908,7 @@ function normalizeSnapshotInput(input) {
|
|||||||
|
|
||||||
function parseSnapshotJson(value) {
|
function parseSnapshotJson(value) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(value);
|
return normalizeEditorSnapshotContract(JSON.parse(value));
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
246
types/contracts.js
Normal file
246
types/contracts.js
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const SECTION_STATUSES = Object.freeze(["editing", "submitted", "approved", "rejected"]);
|
||||||
|
const SECTION_COMMIT_KINDS = Object.freeze(["manual", "restore"]);
|
||||||
|
const SECTION_SUBMISSION_STATUSES = Object.freeze(["pending", "approved", "rejected", "conflicted"]);
|
||||||
|
const SNAPSHOT_OPERATIONS = Object.freeze(["create", "update", "delete", "reference", "replace"]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {"editing"|"submitted"|"approved"|"rejected"} SectionStatus
|
||||||
|
* @typedef {"manual"|"restore"} SectionCommitKind
|
||||||
|
* @typedef {"pending"|"approved"|"rejected"|"conflicted"} SectionSubmissionStatus
|
||||||
|
* @typedef {"create"|"update"|"delete"|"reference"|"replace"} SnapshotOperation
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizeNullableString(value) {
|
||||||
|
if (value === undefined || value === null) return null;
|
||||||
|
const normalized = String(value);
|
||||||
|
return normalized.length ? normalized : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeRequiredString(value, fallback = "") {
|
||||||
|
if (value === undefined || value === null) return fallback;
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeInteger(value, fallback = 0) {
|
||||||
|
const parsed = Number(value);
|
||||||
|
return Number.isFinite(parsed) ? Math.trunc(parsed) : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeStatus(value, allowed, fallback) {
|
||||||
|
const normalized = String(value || "").trim().toLowerCase();
|
||||||
|
return allowed.includes(normalized) ? normalized : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeIdArray(value) {
|
||||||
|
if (!Array.isArray(value)) return [];
|
||||||
|
const seen = new Set();
|
||||||
|
const ids = [];
|
||||||
|
for (const item of value) {
|
||||||
|
if (typeof item !== "string" && typeof item !== "number") continue;
|
||||||
|
const id = String(item).trim();
|
||||||
|
if (!id || seen.has(id)) continue;
|
||||||
|
seen.add(id);
|
||||||
|
ids.push(id);
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeEntityContract(row) {
|
||||||
|
return {
|
||||||
|
id: normalizeRequiredString(row.id),
|
||||||
|
name: normalizeRequiredString(row.name),
|
||||||
|
slug: normalizeNullableString(row.slug),
|
||||||
|
description: normalizeNullableString(row.description),
|
||||||
|
type_id: normalizeNullableString(row.type_id),
|
||||||
|
status: row.status === undefined || row.status === null ? null : normalizeInteger(row.status, 1),
|
||||||
|
created_at: normalizeNullableString(row.created_at),
|
||||||
|
updated_at: normalizeNullableString(row.updated_at),
|
||||||
|
geometry_count: normalizeInteger(row.geometry_count, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeFeaturePropertiesContract(properties) {
|
||||||
|
return {
|
||||||
|
id: properties.id,
|
||||||
|
type: normalizeNullableString(properties.type),
|
||||||
|
time_start: properties.time_start === undefined || properties.time_start === null
|
||||||
|
? null
|
||||||
|
: normalizeInteger(properties.time_start, 0),
|
||||||
|
time_end: properties.time_end === undefined || properties.time_end === null
|
||||||
|
? null
|
||||||
|
: normalizeInteger(properties.time_end, 0),
|
||||||
|
binding: normalizeIdArray(properties.binding),
|
||||||
|
entity_id: normalizeNullableString(properties.entity_id),
|
||||||
|
entity_ids: normalizeIdArray(properties.entity_ids),
|
||||||
|
entity_name: normalizeNullableString(properties.entity_name),
|
||||||
|
entity_names: Array.isArray(properties.entity_names)
|
||||||
|
? properties.entity_names
|
||||||
|
.filter((name) => typeof name === "string" && name.length > 0)
|
||||||
|
: [],
|
||||||
|
entity_type_id: normalizeNullableString(properties.entity_type_id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeFeatureContract(feature) {
|
||||||
|
return {
|
||||||
|
type: "Feature",
|
||||||
|
properties: normalizeFeaturePropertiesContract(feature.properties || {}),
|
||||||
|
geometry: feature.geometry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeFeatureCollectionContract(collection) {
|
||||||
|
return {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: Array.isArray(collection?.features)
|
||||||
|
? collection.features.map(normalizeFeatureContract)
|
||||||
|
: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSectionStateContract(row) {
|
||||||
|
return {
|
||||||
|
section_id: normalizeRequiredString(row.section_id),
|
||||||
|
status: normalizeStatus(row.status, SECTION_STATUSES, "editing"),
|
||||||
|
head_commit_id: normalizeNullableString(row.head_commit_id),
|
||||||
|
version: normalizeInteger(row.version, 0),
|
||||||
|
locked_by: normalizeNullableString(row.locked_by),
|
||||||
|
locked_at: normalizeNullableString(row.locked_at),
|
||||||
|
lock_expires_at: normalizeNullableString(row.lock_expires_at),
|
||||||
|
updated_at: normalizeRequiredString(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSectionContract(row) {
|
||||||
|
return {
|
||||||
|
id: normalizeRequiredString(row.id),
|
||||||
|
title: normalizeRequiredString(row.title),
|
||||||
|
description: normalizeNullableString(row.description),
|
||||||
|
user_id: normalizeNullableString(row.user_id),
|
||||||
|
created_by: normalizeNullableString(row.created_by),
|
||||||
|
created_at: normalizeRequiredString(row.created_at),
|
||||||
|
updated_at: normalizeRequiredString(row.updated_at),
|
||||||
|
state: {
|
||||||
|
status: normalizeStatus(row.status, SECTION_STATUSES, "editing"),
|
||||||
|
head_commit_id: normalizeNullableString(row.head_commit_id),
|
||||||
|
version: normalizeInteger(row.version, 0),
|
||||||
|
locked_by: normalizeNullableString(row.locked_by),
|
||||||
|
locked_at: normalizeNullableString(row.locked_at),
|
||||||
|
lock_expires_at: normalizeNullableString(row.lock_expires_at),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSectionCommitContract(row, snapshot) {
|
||||||
|
const out = {
|
||||||
|
id: normalizeRequiredString(row.id),
|
||||||
|
section_id: normalizeRequiredString(row.section_id),
|
||||||
|
parent_commit_id: normalizeNullableString(row.parent_commit_id),
|
||||||
|
commit_no: normalizeInteger(row.commit_no, 0),
|
||||||
|
kind: normalizeStatus(row.kind, SECTION_COMMIT_KINDS, "manual"),
|
||||||
|
restored_from_commit_id: normalizeNullableString(row.restored_from_commit_id),
|
||||||
|
created_by: normalizeRequiredString(row.created_by),
|
||||||
|
created_at: normalizeRequiredString(row.created_at),
|
||||||
|
title: normalizeNullableString(row.title),
|
||||||
|
note: normalizeNullableString(row.note),
|
||||||
|
snapshot_hash: normalizeNullableString(row.snapshot_hash),
|
||||||
|
};
|
||||||
|
if (arguments.length >= 2) out.snapshot = normalizeEditorSnapshotContract(snapshot);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSectionSubmissionContract(row, snapshot) {
|
||||||
|
const out = {
|
||||||
|
id: normalizeRequiredString(row.id),
|
||||||
|
section_id: normalizeRequiredString(row.section_id),
|
||||||
|
commit_id: normalizeRequiredString(row.commit_id),
|
||||||
|
submitted_by: normalizeRequiredString(row.submitted_by),
|
||||||
|
submitted_at: normalizeRequiredString(row.submitted_at),
|
||||||
|
status: normalizeStatus(row.status, SECTION_SUBMISSION_STATUSES, "pending"),
|
||||||
|
reviewed_by: normalizeNullableString(row.reviewed_by),
|
||||||
|
reviewed_at: normalizeNullableString(row.reviewed_at),
|
||||||
|
review_note: normalizeNullableString(row.review_note),
|
||||||
|
snapshot_hash: normalizeNullableString(row.snapshot_hash),
|
||||||
|
};
|
||||||
|
if (arguments.length >= 2) out.snapshot = normalizeEditorSnapshotContract(snapshot);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSnapshotOperation(value) {
|
||||||
|
return normalizeStatus(value, SNAPSHOT_OPERATIONS, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeEditorSnapshotContract(snapshot) {
|
||||||
|
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) return null;
|
||||||
|
return {
|
||||||
|
schema_version: normalizeInteger(snapshot.schema_version, 1),
|
||||||
|
section: {
|
||||||
|
id: normalizeRequiredString(snapshot.section?.id),
|
||||||
|
title: normalizeRequiredString(snapshot.section?.title),
|
||||||
|
},
|
||||||
|
editor_feature_collection: snapshot.editor_feature_collection
|
||||||
|
? normalizeFeatureCollectionContract(snapshot.editor_feature_collection)
|
||||||
|
: undefined,
|
||||||
|
entities: Array.isArray(snapshot.entities) ? snapshot.entities : [],
|
||||||
|
geometries: Array.isArray(snapshot.geometries) ? snapshot.geometries : [],
|
||||||
|
link_scopes: Array.isArray(snapshot.link_scopes) ? snapshot.link_scopes : [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEditorSnapshotContract(snapshot) {
|
||||||
|
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) {
|
||||||
|
throwContractError("snapshot must be an object");
|
||||||
|
}
|
||||||
|
if (!snapshot.section || typeof snapshot.section !== "object" || Array.isArray(snapshot.section)) {
|
||||||
|
throwContractError("snapshot.section must be an object");
|
||||||
|
}
|
||||||
|
if (!normalizeRequiredString(snapshot.section.id)) {
|
||||||
|
throwContractError("snapshot.section.id is required");
|
||||||
|
}
|
||||||
|
if (!normalizeRequiredString(snapshot.section.title)) {
|
||||||
|
throwContractError("snapshot.section.title is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entity of Array.isArray(snapshot.entities) ? snapshot.entities : []) {
|
||||||
|
const operation = normalizeSnapshotOperation(entity?.operation);
|
||||||
|
if (!operation) throwContractError("Invalid entity operation");
|
||||||
|
if (!normalizeRequiredString(entity?.id)) throwContractError("Entity id is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const geometry of Array.isArray(snapshot.geometries) ? snapshot.geometries : []) {
|
||||||
|
const operation = normalizeSnapshotOperation(geometry?.operation);
|
||||||
|
if (!operation) throwContractError("Invalid geometry operation");
|
||||||
|
if (!normalizeRequiredString(geometry?.id)) throwContractError("Geometry id is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const scope of Array.isArray(snapshot.link_scopes) ? snapshot.link_scopes : []) {
|
||||||
|
if (!normalizeRequiredString(scope?.geometry_id)) throwContractError("link_scope geometry_id is required");
|
||||||
|
if (!Array.isArray(scope?.entity_ids)) throwContractError("link_scope entity_ids must be an array");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwContractError(message) {
|
||||||
|
const err = new Error(message);
|
||||||
|
err.status = 400;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SECTION_STATUSES,
|
||||||
|
SECTION_COMMIT_KINDS,
|
||||||
|
SECTION_SUBMISSION_STATUSES,
|
||||||
|
SNAPSHOT_OPERATIONS,
|
||||||
|
assertEditorSnapshotContract,
|
||||||
|
normalizeEditorSnapshotContract,
|
||||||
|
normalizeEntityContract,
|
||||||
|
normalizeFeatureCollectionContract,
|
||||||
|
normalizeFeatureContract,
|
||||||
|
normalizeIdArray,
|
||||||
|
normalizeSectionCommitContract,
|
||||||
|
normalizeSectionContract,
|
||||||
|
normalizeSectionStateContract,
|
||||||
|
normalizeSectionSubmissionContract,
|
||||||
|
normalizeSnapshotOperation,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user