import fs from 'node:fs'; const id = { name: 'id', in: 'path', required: true, schema: { type: 'string', format: 'uuid' }, }; const string = { type: 'string' }; const schema = (properties) => ({ type: 'object', properties }); const paths = {}; const operation = (summary, parameters = [], body = null) => ({ summary, security: [{ laravelSession: [] }], parameters: [ ...parameters, ...(body ? [ { name: 'Idempotency-Key', in: 'header', required: true, schema: { type: 'string', format: 'uuid' }, }, ] : []), ], ...(body ? { requestBody: { required: true, content: { 'application/json': { schema: body } }, }, } : {}), responses: Object.fromEntries( [ 200, ...(summary.startsWith('Create source') ? [201] : []), 401, 403, 404, 409, 422, 429, ].map((code) => [ code, { description: code < 300 ? 'JSON result with X-Correlation-ID; private, no-store' : 'Request rejected; validation and workflow errors use Problem Details within territorial middleware', }, ]), ), }); for (const collection of [ 'countries', 'provinces', 'districts', 'admin-posts', 'localities', 'search', ]) paths['/' + collection] = { get: operation( 'Read currently published territories: ' + collection, [ 'q', 'cursor', 'parent_id', 'country_id', 'province_id', 'district_id', 'admin_post_id', ] .map((name) => ({ name, in: 'query', schema: string })) .concat([ { name: 'limit', in: 'query', schema: { type: 'integer', minimum: 1, maximum: 100 }, }, ]), ), }; paths['/territories/{type}/{id}'] = { get: operation('Read published territory and translated names', [ id, { name: 'type', in: 'path', required: true, schema: { enum: [ 'country', 'province', 'district', 'admin_post', 'locality', ], }, }, ]), }; const territory = schema({ name: string, code: string, location_type: { enum: ['country', 'province', 'district', 'admin_post', 'locality'], }, parent_id: { type: ['string', 'null'], format: 'uuid' }, source_id: { type: 'string', format: 'uuid' }, valid_from: { type: 'string', format: 'date' }, valid_to: { type: ['string', 'null'], format: 'date' }, reason: { type: 'string', minLength: 10, maxLength: 1000 }, }); territory.required = [ 'name', 'code', 'location_type', 'source_id', 'valid_from', 'reason', ]; paths['/territories'] = { post: operation( 'Create private territorial draft; designated editor with MFA', [], territory, ), }; paths['/territories/{id}'] = { patch: operation( 'Replace draft attributes and invalidate editorial approvals', [id], { ...territory, required: [...territory.required, 'version_no'], properties: { ...territory.properties, version_no: { type: 'integer', minimum: 1 }, }, }, ), }; paths['/territories/{id}/transitions'] = { post: operation( 'Submit, review, publish, reject or archive; independent actors required', [id], { ...schema({ action: { enum: [ 'pending_review', 'approved', 'published', 'rejected', 'archived', ], }, version_no: { type: 'integer', minimum: 1 }, reason: { type: 'string', minLength: 10 }, }), required: ['action', 'version_no', 'reason'], }, ), }; paths['/sources'] = { post: operation('Create source in pending state', [], { ...schema({ name: string, responsible_entity: string, reference: string, license: string, checksum: { type: 'string', pattern: '^[0-9a-f]{64}$' }, source_version: string, collected_at: { type: 'string', format: 'date' }, original_crs: string, }), required: [ 'name', 'reference', 'checksum', 'source_version', 'collected_at', ], }), }; paths['/sources/{id}/transitions'] = { post: operation( 'Independent source approval or revocation with evidence reference', [id], { ...schema({ action: { enum: ['approved', 'revoked'] }, approval_reference: { type: 'string', minLength: 10 }, }), required: ['action', 'approval_reference'], }, ), }; paths['/sources'].get = operation( 'Management-only source metadata and decision evidence, MFA required', ); paths['/drafts'] = { get: operation( 'Management-only territorial editorial records, MFA required', ), }; fs.writeFileSync( 'docs/investmoz/gis/territorial-openapi.json', JSON.stringify( { openapi: '3.1.0', info: { title: 'InvestMoz territorial registry — implemented subset', version: '0.1.0', description: 'Local session adapter. OAuth/OIDC, geometric queries, assignments, imports, hierarchy endpoint and administrative UI remain unimplemented. Response schemas require expansion before external certification.', }, servers: [{ url: '/api/v1/geo' }], paths, components: { securitySchemes: { laravelSession: { type: 'apiKey', in: 'cookie', name: 'deployment_session_cookie', description: 'Actual name configured by Laravel; write requests also require Laravel CSRF/origin protection. This is not OAuth/OIDC.', }, }, }, }, null, 2, ) + '\n', ); console.log('Territorial OpenAPI: ' + Object.keys(paths).length + ' paths');