periplus-map 0.2.1__py3-none-any.whl
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.
- periplus/__init__.py +70 -0
- periplus/checkmap.py +80 -0
- periplus/cli.py +658 -0
- periplus/contract/schema/map.schema.json +138 -0
- periplus/contract/schema/navigation.schema.json +200 -0
- periplus/contract/schema/pack-file.schema.json +1405 -0
- periplus/contract/schema/pack-manifest.schema.json +245 -0
- periplus/contract/schema/project-settings.schema.json +85 -0
- periplus/contract/spec/engine.md +151 -0
- periplus/contract/spec/rules.md +352 -0
- periplus/detect.py +228 -0
- periplus/engine/__init__.py +5 -0
- periplus/engine/check.py +481 -0
- periplus/engine/emit.py +368 -0
- periplus/engine/packload.py +2321 -0
- periplus/engine/pattern.py +27 -0
- periplus/engine/rules.py +1946 -0
- periplus/engine/select.py +491 -0
- periplus/engine/source.py +172 -0
- periplus/engine/tree.py +424 -0
- periplus/errors.py +332 -0
- periplus/init.py +358 -0
- periplus/layout.py +28 -0
- periplus/manifest.py +498 -0
- periplus/map.py +364 -0
- periplus/navigate/__init__.py +1 -0
- periplus/navigate/find.py +143 -0
- periplus/navigate/ids.py +30 -0
- periplus/navigate/listing.py +42 -0
- periplus/navigate/mapfile.py +189 -0
- periplus/navigate/path.py +175 -0
- periplus/navigate/show.py +175 -0
- periplus/navigate/types.py +83 -0
- periplus/navigate/walk.py +138 -0
- periplus/packs/advancedqueue_basic@0.0.1/pack.yaml +14 -0
- periplus/packs/advancedqueue_basic@0.0.1/queue_job_type/queue_job_type.yaml +9 -0
- periplus/packs/advancedqueue_basic@0.0.1/queue_job_type/queue_job_type_annotation.yaml +43 -0
- periplus/packs/advancedqueue_basic@0.0.1/queue_job_type/queue_job_type_attribute.yaml +43 -0
- periplus/packs/ai_basic@0.0.1/ai_agent/ai_agent.yaml +8 -0
- periplus/packs/ai_basic@0.0.1/ai_agent/ai_agent_attribute.yaml +43 -0
- periplus/packs/ai_basic@0.0.1/ai_function_call/ai_function_call.yaml +8 -0
- periplus/packs/ai_basic@0.0.1/ai_function_call/ai_function_call_attribute.yaml +43 -0
- periplus/packs/ai_basic@0.0.1/pack.yaml +15 -0
- periplus/packs/config_pages_basic@0.0.1/bundle/config_pages_bundle_config.yaml +54 -0
- periplus/packs/config_pages_basic@0.0.1/pack.yaml +11 -0
- periplus/packs/crop_basic@0.0.1/bundle/crop_bundle_config.yaml +54 -0
- periplus/packs/crop_basic@0.0.1/pack.yaml +11 -0
- periplus/packs/drupal@0.1.0/block/block_placement.yaml +25 -0
- periplus/packs/drupal@0.1.0/block/block_placement_code.yaml +58 -0
- periplus/packs/drupal@0.1.0/block/block_placement_config.yaml +53 -0
- periplus/packs/drupal@0.1.0/block/block_plugin.yaml +52 -0
- periplus/packs/drupal@0.1.0/block/block_plugin_altered.yaml +61 -0
- periplus/packs/drupal@0.1.0/block/block_plugin_annotation.yaml +69 -0
- periplus/packs/drupal@0.1.0/block/block_plugin_attribute.yaml +56 -0
- periplus/packs/drupal@0.1.0/block/block_plugin_content.yaml +46 -0
- periplus/packs/drupal@0.1.0/block/block_plugin_derived.yaml +110 -0
- periplus/packs/drupal@0.1.0/block/block_plugin_deriver.yaml +30 -0
- periplus/packs/drupal@0.1.0/block/block_type.yaml +14 -0
- periplus/packs/drupal@0.1.0/block/block_type_code.yaml +43 -0
- periplus/packs/drupal@0.1.0/block/block_type_config.yaml +45 -0
- periplus/packs/drupal@0.1.0/cache/cache.yaml +18 -0
- periplus/packs/drupal@0.1.0/cache/cache_backend.yaml +31 -0
- periplus/packs/drupal@0.1.0/component/sdc.yaml +19 -0
- periplus/packs/drupal@0.1.0/component/sdc_definition.yaml +25 -0
- periplus/packs/drupal@0.1.0/component/sdc_use.yaml +25 -0
- periplus/packs/drupal@0.1.0/config/config_object.yaml +65 -0
- periplus/packs/drupal@0.1.0/display/display.yaml +32 -0
- periplus/packs/drupal@0.1.0/display/display_code.yaml +53 -0
- periplus/packs/drupal@0.1.0/display/display_config.yaml +95 -0
- periplus/packs/drupal@0.1.0/entity/entity.yaml +33 -0
- periplus/packs/drupal@0.1.0/entity/entity_class.yaml +60 -0
- periplus/packs/drupal@0.1.0/entity/entity_save.yaml +27 -0
- periplus/packs/drupal@0.1.0/entity/entity_type_annotation.yaml +85 -0
- periplus/packs/drupal@0.1.0/entity/entity_type_attribute.yaml +76 -0
- periplus/packs/drupal@0.1.0/field/field_instance_code.yaml +40 -0
- periplus/packs/drupal@0.1.0/field/field_instance_config.yaml +58 -0
- periplus/packs/drupal@0.1.0/field/field_storage.yaml +37 -0
- periplus/packs/drupal@0.1.0/field/field_storage_code.yaml +39 -0
- periplus/packs/drupal@0.1.0/field/field_storage_config.yaml +24 -0
- periplus/packs/drupal@0.1.0/form/form.yaml +48 -0
- periplus/packs/drupal@0.1.0/form/form_class.yaml +50 -0
- periplus/packs/drupal@0.1.0/form/form_id.yaml +48 -0
- periplus/packs/drupal@0.1.0/form/form_methods.yaml +39 -0
- periplus/packs/drupal@0.1.0/hook/hook.yaml +8 -0
- periplus/packs/drupal@0.1.0/hook/hook_attribute.yaml +28 -0
- periplus/packs/drupal@0.1.0/module/module.yaml +8 -0
- periplus/packs/drupal@0.1.0/module/module_info.yaml +50 -0
- periplus/packs/drupal@0.1.0/pack.yaml +184 -0
- periplus/packs/drupal@0.1.0/plugin/plugin.yaml +17 -0
- periplus/packs/drupal@0.1.0/plugin/plugin_annotation.yaml +120 -0
- periplus/packs/drupal@0.1.0/recommendations/recommendations.yaml +14 -0
- periplus/packs/drupal@0.1.0/service/service.yaml +19 -0
- periplus/packs/drupal@0.1.0/service/service_autowire.yaml +28 -0
- periplus/packs/drupal@0.1.0/service/service_definition.yaml +24 -0
- periplus/packs/drupal@0.1.0/service/service_use.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/action/action.yaml +9 -0
- periplus/packs/drupal_basic@0.3.0/action/action_annotation.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/action/action_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/block/block_placement.yaml +16 -0
- periplus/packs/drupal_basic@0.3.0/block/block_placement_config.yaml +51 -0
- periplus/packs/drupal_basic@0.3.0/block_plugin/block_plugin.yaml +14 -0
- periplus/packs/drupal_basic@0.3.0/block_plugin/block_plugin_annotation.yaml +47 -0
- periplus/packs/drupal_basic@0.3.0/block_plugin/block_plugin_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/block_type/block_type.yaml +8 -0
- periplus/packs/drupal_basic@0.3.0/block_type/block_type_config.yaml +54 -0
- periplus/packs/drupal_basic@0.3.0/boundary/boundary_types.yaml +19 -0
- periplus/packs/drupal_basic@0.3.0/bundle/bundle.yaml +20 -0
- periplus/packs/drupal_basic@0.3.0/bundle/bundle_config.yaml +393 -0
- periplus/packs/drupal_basic@0.3.0/call/call.yaml +349 -0
- periplus/packs/drupal_basic@0.3.0/component/component.yaml +30 -0
- periplus/packs/drupal_basic@0.3.0/config/config_object.yaml +119 -0
- periplus/packs/drupal_basic@0.3.0/constraint/constraint.yaml +8 -0
- periplus/packs/drupal_basic@0.3.0/constraint/constraint_annotation.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/constraint/constraint_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/display/display.yaml +30 -0
- periplus/packs/drupal_basic@0.3.0/display/display_config.yaml +203 -0
- periplus/packs/drupal_basic@0.3.0/display/display_mode_config.yaml +60 -0
- periplus/packs/drupal_basic@0.3.0/entity_type/config_entity_type_annotation.yaml +46 -0
- periplus/packs/drupal_basic@0.3.0/entity_type/config_entity_type_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/entity_type/content_entity_type_annotation.yaml +46 -0
- periplus/packs/drupal_basic@0.3.0/entity_type/content_entity_type_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/entity_type/entity_type.yaml +16 -0
- periplus/packs/drupal_basic@0.3.0/field/field_instance_config.yaml +116 -0
- periplus/packs/drupal_basic@0.3.0/field/field_storage.yaml +21 -0
- periplus/packs/drupal_basic@0.3.0/field/field_storage_config.yaml +45 -0
- periplus/packs/drupal_basic@0.3.0/field_formatter/field_formatter.yaml +8 -0
- periplus/packs/drupal_basic@0.3.0/field_formatter/field_formatter_annotation.yaml +46 -0
- periplus/packs/drupal_basic@0.3.0/field_formatter/field_formatter_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/field_type/field_type_annotation.yaml +46 -0
- periplus/packs/drupal_basic@0.3.0/field_type/field_type_attribute.yaml +50 -0
- periplus/packs/drupal_basic@0.3.0/field_widget/field_widget_annotation.yaml +46 -0
- periplus/packs/drupal_basic@0.3.0/field_widget/field_widget_attribute.yaml +50 -0
- periplus/packs/drupal_basic@0.3.0/form/form_id.yaml +80 -0
- periplus/packs/drupal_basic@0.3.0/hook/hook.yaml +100 -0
- periplus/packs/drupal_basic@0.3.0/hook/hook_attribute.yaml +64 -0
- periplus/packs/drupal_basic@0.3.0/hook/hook_theme_include.yaml +34 -0
- periplus/packs/drupal_basic@0.3.0/library/library.yaml +12 -0
- periplus/packs/drupal_basic@0.3.0/library/library_definition.yaml +58 -0
- periplus/packs/drupal_basic@0.3.0/local_action/local_action.yaml +12 -0
- periplus/packs/drupal_basic@0.3.0/local_action/local_action_definition.yaml +49 -0
- periplus/packs/drupal_basic@0.3.0/mail/mail.yaml +8 -0
- periplus/packs/drupal_basic@0.3.0/mail/mail_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/media_source/media_source.yaml +8 -0
- periplus/packs/drupal_basic@0.3.0/media_source/media_source_annotation.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/media_source/media_source_attribute.yaml +79 -0
- periplus/packs/drupal_basic@0.3.0/menu_link/menu_link.yaml +18 -0
- periplus/packs/drupal_basic@0.3.0/menu_link/menu_link_definition.yaml +48 -0
- periplus/packs/drupal_basic@0.3.0/module/module.yaml +7 -0
- periplus/packs/drupal_basic@0.3.0/module/module_info.yaml +58 -0
- periplus/packs/drupal_basic@0.3.0/pack.yaml +528 -0
- periplus/packs/drupal_basic@0.3.0/permission/permission.yaml +7 -0
- periplus/packs/drupal_basic@0.3.0/permission/permission_definition.yaml +27 -0
- periplus/packs/drupal_basic@0.3.0/queue_worker/queue_worker.yaml +9 -0
- periplus/packs/drupal_basic@0.3.0/queue_worker/queue_worker_annotation.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/queue_worker/queue_worker_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/rest_resource/rest_resource.yaml +8 -0
- periplus/packs/drupal_basic@0.3.0/rest_resource/rest_resource_annotation.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/rest_resource/rest_resource_attribute.yaml +43 -0
- periplus/packs/drupal_basic@0.3.0/role/role.yaml +14 -0
- periplus/packs/drupal_basic@0.3.0/role/role_config.yaml +44 -0
- periplus/packs/drupal_basic@0.3.0/route/route.yaml +42 -0
- periplus/packs/drupal_basic@0.3.0/route/route_definition.yaml +113 -0
- periplus/packs/drupal_basic@0.3.0/route/route_options.yaml +55 -0
- periplus/packs/drupal_basic@0.3.0/route/route_requirements.yaml +108 -0
- periplus/packs/drupal_basic@0.3.0/service/service.yaml +56 -0
- periplus/packs/drupal_basic@0.3.0/service/service_definition.yaml +181 -0
- periplus/packs/drupal_basic@0.3.0/template/template.yaml +86 -0
- periplus/packs/drupal_basic@0.3.0/template/template_route_link.yaml +30 -0
- periplus/packs/drupal_basic@0.3.0/theme/theme.yaml +15 -0
- periplus/packs/drupal_basic@0.3.0/theme/theme_info.yaml +61 -0
- periplus/packs/drupal_basic@0.3.0/theme_hook/template_suggestion.yaml +29 -0
- periplus/packs/drupal_basic@0.3.0/theme_hook/theme_hook.yaml +87 -0
- periplus/packs/drupal_basic@0.3.0/theme_hook/theme_hook_render.yaml +20 -0
- periplus/packs/drupal_basic@0.3.0/url/url.yaml +88 -0
- periplus/packs/drupal_basic@0.3.0/views_plugin/views_field_attribute.yaml +80 -0
- periplus/packs/drupal_basic@0.3.0/views_plugin/views_filter_attribute.yaml +80 -0
- periplus/packs/drupal_basic@0.3.0/views_plugin/views_plugin.yaml +15 -0
- periplus/packs/drupal_basic@0.3.0/views_plugin/views_style_attribute.yaml +80 -0
- periplus/packs/drupal_js_basic@0.0.1/attachment/attachment.yaml +12 -0
- periplus/packs/drupal_js_basic@0.0.1/attachment/attachment_in_php.yaml +15 -0
- periplus/packs/drupal_js_basic@0.0.1/attachment/attachment_in_php_array.yaml +52 -0
- periplus/packs/drupal_js_basic@0.0.1/attachment/attachment_in_twig.yaml +26 -0
- periplus/packs/drupal_js_basic@0.0.1/attachment/php_function_holder.yaml +13 -0
- periplus/packs/drupal_js_basic@0.0.1/behavior/behavior.yaml +198 -0
- periplus/packs/drupal_js_basic@0.0.1/pack.yaml +153 -0
- periplus/packs/drupal_js_basic@0.0.1/setting/setting.yaml +13 -0
- periplus/packs/drupal_js_basic@0.0.1/setting/setting_in_js.yaml +15 -0
- periplus/packs/drupal_js_basic@0.0.1/setting/setting_in_php.yaml +92 -0
- periplus/packs/drush_basic@0.0.1/command/command.yaml +15 -0
- periplus/packs/drush_basic@0.0.1/command/command_annotation.yaml +60 -0
- periplus/packs/drush_basic@0.0.1/command/command_attribute.yaml +88 -0
- periplus/packs/drush_basic@0.0.1/pack.yaml +29 -0
- periplus/packs/eck_basic@0.0.1/bundle/eck_bundle_config.yaml +56 -0
- periplus/packs/eck_basic@0.0.1/entity_type/eck_entity_type_config.yaml +26 -0
- periplus/packs/eck_basic@0.0.1/pack.yaml +28 -0
- periplus/packs/go@0.0.2/interfaces/satisfies.yaml +47 -0
- periplus/packs/go@0.0.2/pack.yaml +51 -0
- periplus/packs/go@0.0.2/types/find_declarations.yaml +137 -0
- periplus/packs/go@0.0.2/types/node_types.yaml +36 -0
- periplus/packs/go_basic@0.0.3/pack.yaml +104 -0
- periplus/packs/go_basic@0.0.3/rules/calls.yaml +25 -0
- periplus/packs/go_basic@0.0.3/rules/declarations.yaml +40 -0
- periplus/packs/go_basic@0.0.3/rules/imports.yaml +16 -0
- periplus/packs/go_basic@0.0.3/types/node_types.yaml +15 -0
- periplus/packs/js_basic@0.0.1/function/function.yaml +68 -0
- periplus/packs/js_basic@0.0.1/pack.yaml +50 -0
- periplus/packs/laravel_basic@0.0.5/pack.yaml +313 -0
- periplus/packs/laravel_basic@0.0.5/rules/classes.yaml +42 -0
- periplus/packs/laravel_basic@0.0.5/rules/config.yaml +229 -0
- periplus/packs/laravel_basic@0.0.5/rules/routes.yaml +210 -0
- periplus/packs/laravel_basic@0.0.5/rules/types.yaml +50 -0
- periplus/packs/laravel_basic@0.0.5/rules/views.yaml +100 -0
- periplus/packs/paragraphs_basic@0.0.1/bundle/paragraph_bundle_config.yaml +54 -0
- periplus/packs/paragraphs_basic@0.0.1/pack.yaml +12 -0
- periplus/packs/php@0.1.0/edges/edge_kinds.yaml +64 -0
- periplus/packs/php@0.1.0/edges/find_calls.yaml +34 -0
- periplus/packs/php@0.1.0/edges/find_imports.yaml +18 -0
- periplus/packs/php@0.1.0/edges/find_member_access.yaml +22 -0
- periplus/packs/php@0.1.0/edges/find_structure.yaml +38 -0
- periplus/packs/php@0.1.0/embedded/embedded_regions.yaml +19 -0
- periplus/packs/php@0.1.0/imports/imports.yaml +20 -0
- periplus/packs/php@0.1.0/pack.yaml +117 -0
- periplus/packs/php@0.1.0/resolution/excluded_tokens.yaml +23 -0
- periplus/packs/php@0.1.0/resolution/resolution.yaml +35 -0
- periplus/packs/php@0.1.0/types/find_declarations.yaml +102 -0
- periplus/packs/php@0.1.0/types/node_types.yaml +55 -0
- periplus/packs/php_basic@0.2.0/edges/edge_kinds.yaml +48 -0
- periplus/packs/php_basic@0.2.0/edges/find_structure.yaml +750 -0
- periplus/packs/php_basic@0.2.0/imports/imports.yaml +19 -0
- periplus/packs/php_basic@0.2.0/pack.yaml +94 -0
- periplus/packs/php_basic@0.2.0/types/find_declarations.yaml +267 -0
- periplus/packs/php_basic@0.2.0/types/node_types.yaml +55 -0
- periplus/packs/profile_basic@0.0.1/bundle/profile_bundle_config.yaml +54 -0
- periplus/packs/profile_basic@0.0.1/pack.yaml +12 -0
- periplus/packs/salesforce_basic@0.0.1/event/salesforce_event.yaml +36 -0
- periplus/packs/salesforce_basic@0.0.1/pack.yaml +31 -0
- periplus/packs/salesforce_basic@0.0.1/salesforce_mapping/salesforce_mapping.yaml +19 -0
- periplus/packs/salesforce_basic@0.0.1/salesforce_mapping/salesforce_mapping_config.yaml +83 -0
- periplus/packs/salesforce_basic@0.0.1/salesforce_mapping_field/salesforce_mapping_field.yaml +8 -0
- periplus/packs/salesforce_basic@0.0.1/salesforce_mapping_field/salesforce_mapping_field_annotation.yaml +43 -0
- periplus/packs/twig@0.0.1/pack.yaml +26 -0
- periplus/packs/twig@0.0.1/types/template.yaml +57 -0
- periplus/packs/twig_basic@0.1.0/pack.yaml +11 -0
- periplus/packs/twig_tweak_basic@0.0.1/pack.yaml +27 -0
- periplus/packs/twig_tweak_basic@0.0.1/rules/block.yaml +20 -0
- periplus/packs/twig_tweak_basic@0.0.1/rules/entity.yaml +35 -0
- periplus/packs/twig_tweak_basic@0.0.1/rules/field.yaml +21 -0
- periplus/packs/twig_tweak_basic@0.0.1/rules/menu.yaml +21 -0
- periplus/packs/webform_basic@0.0.1/bundle/webform_submission_bundle_config.yaml +54 -0
- periplus/packs/webform_basic@0.0.1/pack.yaml +20 -0
- periplus/packs/webform_basic@0.0.1/webform_handler/webform_handler.yaml +8 -0
- periplus/packs/webform_basic@0.0.1/webform_handler/webform_handler_annotation.yaml +43 -0
- periplus/packs/yaml@0.0.1/pack.yaml +72 -0
- periplus/packs/yaml@0.0.1/types/find_mapping_entry.yaml +21 -0
- periplus/packs/yaml@0.0.1/types/yaml_types.yaml +44 -0
- periplus/packs/yaml_basic@0.1.0/pack.yaml +10 -0
- periplus/packs.py +447 -0
- periplus/preflight.py +102 -0
- periplus/py.typed +0 -0
- periplus/report.py +596 -0
- periplus/resolution.py +355 -0
- periplus/settings.py +617 -0
- periplus/settings.stub.yml +99 -0
- periplus/spec.py +774 -0
- periplus/update.py +197 -0
- periplus/validate.py +655 -0
- periplus_map-0.2.1.dist-info/METADATA +566 -0
- periplus_map-0.2.1.dist-info/RECORD +272 -0
- periplus_map-0.2.1.dist-info/WHEEL +4 -0
- periplus_map-0.2.1.dist-info/entry_points.txt +2 -0
- periplus_map-0.2.1.dist-info/licenses/LICENSE +202 -0
- periplus_map-0.2.1.dist-info/licenses/NOTICE +2 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://periplus.dev/schema/0/pack-manifest.schema.json",
|
|
4
|
+
"title": "Periplus pack manifest",
|
|
5
|
+
"description": "pack.yaml, one per pack directory. Holds the pack's identity, where having two answers is a conflict rather than a merge. Everything else in a pack is a contributed file and merges additively.",
|
|
6
|
+
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": ["pack", "version"],
|
|
10
|
+
"properties": {
|
|
11
|
+
|
|
12
|
+
"$schema": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "The contract this manifest is written against, by the `$id` of the schema above. Allowed here because the top level refuses every key it does not declare, so a file carrying the key an editor reads would otherwise be invalid for saying which rules it follows. What the key claims is which contract the file was authored against; it does not claim that anything resolves the URL today."
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
"pack": {"type": "string", "pattern": "^[a-z][a-z0-9_]*$"},
|
|
18
|
+
"version": {"type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$"},
|
|
19
|
+
|
|
20
|
+
"depends": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": {"type": "string", "pattern": "^[a-z][a-z0-9_]*$"},
|
|
23
|
+
"description": "Zero or more packs, forming a directed acyclic graph rather than a chain: Next.js sits on React and on the Node runtime at once. Because a graph has no single load order, contributions must merge additively and without depending on order. There are no kinds of pack — a root is one with nothing beneath it, and it need not be a language."
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
"detect": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"required": ["any_of"],
|
|
30
|
+
"description": "How a repository is recognised as needing this pack, so `periplus init` can prefill a pin the person then edits. Declared here and never in engine code: a third-party pack is detected exactly as a bundled one is, which is what makes a new stack a pack rather than a release. A pack that declares no detector is never suggested — owning an extension is not the same as being a stack anybody pins, and a language pack firing on every file with its extension would prefill noise into every settings file there is.",
|
|
31
|
+
"properties": {
|
|
32
|
+
"any_of": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"minItems": 1,
|
|
35
|
+
"description": "The alternatives, tried in the order written. A repository is Drupal if its composer.json requires Drupal core OR it has a docroot, so the block names its combining rule rather than leaving a bare list for a reader to guess at. An empty list detects nothing and is refused here, where the author sees it, rather than at the one call site that would notice.",
|
|
36
|
+
"items": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"additionalProperties": false,
|
|
39
|
+
"required": ["path"],
|
|
40
|
+
"description": "One alternative: a repository-relative path that must exist as a file, and optionally a literal substring of that file's text. `contains` is a substring and not a regular expression, because detection reads third-party pack data with no time limit around it and a substring has no backtracking cost. The path stays under the repository root — a signal that leaves it never fires, since a detector that reads outside the checkout turns a committed settings file into a readout of the machine that ran `init`. Both levels are closed: an author who writes `contain:` gets a refusal, not a detector that silently degrades to an existence check and fires on every repository in the language.",
|
|
41
|
+
"properties": {
|
|
42
|
+
"path": {"type": "string"},
|
|
43
|
+
"contains": {"type": "string"}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
"grammar": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
"required": ["provider", "provider_version", "language", "grammar_version"],
|
|
54
|
+
"description": "Only a pack that owns a language declares this. A domain pack gets its grammars by depending on packs that own them, because a domain pack owning a language means every domain pack touching that language redeclares it and they drift.",
|
|
55
|
+
"properties": {
|
|
56
|
+
"provider": {"enum": ["tree-sitter"]},
|
|
57
|
+
"provider_version": {"$ref": "#/$defs/exactVersion"},
|
|
58
|
+
"language": {"type": "string"},
|
|
59
|
+
"grammar_version": {"$ref": "#/$defs/exactVersion"},
|
|
60
|
+
"constant_access": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"required": ["node", "scope", "name"],
|
|
64
|
+
"description": "A class constant read as a call's or an attribute's argument: the tree node type of the access, the node types its first named child, the scope, may have, and the node types its last named child, the constant's name, may have. A scope that is a written name resolves as one. A scope whose text enclosing lists is the enclosing class, and any other scope stands for no text; without enclosing, no scope is. The argument then stands for the text the run read declared for that constant. A pack that reads the file type may declare it, and the first such pack in load order is used.",
|
|
65
|
+
"properties": {
|
|
66
|
+
"node": {"type": "string", "minLength": 1},
|
|
67
|
+
"scope": {"type": "array", "items": {"type": "string"}, "minItems": 1},
|
|
68
|
+
"name": {"type": "array", "items": {"type": "string"}, "minItems": 1},
|
|
69
|
+
"enclosing": {"type": "array", "items": {"type": "string"}, "minItems": 1}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"constant_declaration": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"required": ["node", "name", "value"],
|
|
76
|
+
"description": "A class constant declared in a class body: the tree node type of one declaration, the node types of its first named child, the name, and those of its last named child, the value. A value that is a text literal by the identity block's call literal keys is collected, under the full name of the enclosing type declaration and the constant's name.",
|
|
77
|
+
"properties": {
|
|
78
|
+
"node": {"type": "string", "minLength": 1},
|
|
79
|
+
"name": {"type": "array", "items": {"type": "string"}, "minItems": 1},
|
|
80
|
+
"value": {"type": "array", "items": {"type": "string"}, "minItems": 1}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"dependentRequired": {
|
|
85
|
+
"constant_access": ["constant_declaration"],
|
|
86
|
+
"constant_declaration": ["constant_access"]
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
"files": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"additionalProperties": false,
|
|
93
|
+
"properties": {
|
|
94
|
+
"extends": {"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}]},
|
|
95
|
+
"extensions": {"type": "array", "items": {"type": "string"}},
|
|
96
|
+
"add_extensions": {"type": "array", "items": {"type": "string"}},
|
|
97
|
+
"exclude": {"type": "array", "items": {"type": "string"}},
|
|
98
|
+
"add_excludes": {"type": "array", "items": {"type": "string"}},
|
|
99
|
+
"cache_key_includes": {"type": "array", "items": {"type": "string"}},
|
|
100
|
+
"types": {
|
|
101
|
+
"type": "object",
|
|
102
|
+
"propertyNames": {"pattern": "^[a-z][a-z0-9_.]*$"},
|
|
103
|
+
"additionalProperties": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"additionalProperties": false,
|
|
106
|
+
"required": ["reader"],
|
|
107
|
+
"anyOf": [{"required": ["endings"]}, {"required": ["names"]}],
|
|
108
|
+
"if": {"properties": {"reader": {"const": "data"}}},
|
|
109
|
+
"then": {"required": ["format"]},
|
|
110
|
+
"else": {"not": {"required": ["format"]}},
|
|
111
|
+
"properties": {
|
|
112
|
+
"endings": {"type": "array", "items": {"type": "string", "pattern": "^\\.?[^./][^/]*$"}, "minItems": 1},
|
|
113
|
+
"names": {"type": "array", "items": {"type": "string", "pattern": "^[^/]+$"}, "minItems": 1},
|
|
114
|
+
"reader": {"enum": ["data", "text", "tree"]},
|
|
115
|
+
"format": {"type": "string", "description": "The data format a type read as data is written in, by the name of one of the engine's data readers: yaml. Required with reader data and refused with any other reader. The short form extensions is read as yaml."},
|
|
116
|
+
"path_values": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"propertyNames": {"pattern": "^[a-z][a-z0-9_]*$"},
|
|
119
|
+
"additionalProperties": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"required": ["pattern", "template"],
|
|
123
|
+
"properties": {
|
|
124
|
+
"pattern": {"type": "string"},
|
|
125
|
+
"template": {"type": "string"},
|
|
126
|
+
"normalize": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["replace", "with"], "properties": {"replace": {"type": "string"}, "with": {"type": "string"}}}},
|
|
127
|
+
"case": {"enum": ["lower", "upper"]},
|
|
128
|
+
"relative_to": {"type": "string", "description": "A folder name: the pattern is searched in the path below the deepest directory of that folder that holds the file, and the value is absent for a file no directory of it holds. A folder whose value holds ** is refused at load."}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"description": "Values computed once per file of this type from its path from the project folder, forward slashes: the pattern is searched there and the template is filled from its named captures and the project's settings values, then normalize and case reshape it; a template naming a name that is neither, or both, is refused. A pattern that does not match leaves the value absent. Every rule on a file of this type may name the value in a template."
|
|
132
|
+
},
|
|
133
|
+
"sections": {
|
|
134
|
+
"type": "array",
|
|
135
|
+
"items": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"additionalProperties": false,
|
|
138
|
+
"required": ["name", "open", "close"],
|
|
139
|
+
"properties": {
|
|
140
|
+
"name": {"type": "string"},
|
|
141
|
+
"open": {"type": "string"},
|
|
142
|
+
"close": {"type": "string"}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"description": "Spans of the file's text a text rule does not read unless its match names the section under reads_sections. Found in one pass from the start: the earliest opening wins, the first listed on a tie, and the section runs to the end of the first closing match after it, or to the end of the file. In close, a braced name stands for the text the named capture of open matched, escaped."
|
|
146
|
+
},
|
|
147
|
+
"spans": {
|
|
148
|
+
"type": "array",
|
|
149
|
+
"items": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"additionalProperties": false,
|
|
152
|
+
"required": ["name", "open", "balance"],
|
|
153
|
+
"properties": {
|
|
154
|
+
"name": {"type": "string"},
|
|
155
|
+
"open": {"type": "string"},
|
|
156
|
+
"balance": {"type": "array", "items": {"type": "string", "minLength": 1, "maxLength": 1}, "minItems": 2, "maxItems": 2}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"description": "Regions of the file's text a text rule's value reads with from: {enclosing: <name>, capture: <capture>}. A span starts at each match of open outside the sections. From the end of that match, each balance[0] outside the sections counts one up and each balance[1] one down; the span ends after the closing character that brings the count to zero or below, or at the end of the file. Spans of one name nest."
|
|
160
|
+
},
|
|
161
|
+
"resolve": {
|
|
162
|
+
"type": "array",
|
|
163
|
+
"minItems": 1,
|
|
164
|
+
"items": {
|
|
165
|
+
"oneOf": [
|
|
166
|
+
{"enum": ["bound_first_segment", "as_written"]},
|
|
167
|
+
{"type": "object", "additionalProperties": false, "required": ["full_if_prefixed"], "properties": {"full_if_prefixed": {"type": "string", "minLength": 1}}},
|
|
168
|
+
{"type": "object", "additionalProperties": false, "required": ["prefix_with"], "properties": {"prefix_with": {"type": "string", "minLength": 1}}},
|
|
169
|
+
{"type": "object", "additionalProperties": false, "required": ["prefix_bare_with"], "properties": {"prefix_bare_with": {"type": "string", "minLength": 1}}}
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
"description": "The resolution steps an edge end of a text rule on a file of this type takes when the end declares none: see resolveStep in the pack file schema."
|
|
173
|
+
},
|
|
174
|
+
"separator": {"type": "string", "description": "The separator beside the file type's resolve."}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"description": "Each file type by name. An ending is matched against the end of a file name after a dot, with or without its own leading dot, and the longest ending any loaded pack claims wins; a whole file name claims a file without an ending. The reader says how a rule reads the file: data, text, or tree, which needs the pack's grammar. The short form extensions is one type named by each listed ending, read as a tree when the pack pins a grammar and as data otherwise."
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"description": "Exclude is not boundary. A boundary file is unread but the things inside it can still be nodes, because first-party code names them. An excluded file does not exist as far as the map is concerned, not even as the target of a reference, and it is enforced at file selection before anything parses."
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
"folders": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"additionalProperties": {
|
|
186
|
+
"oneOf": [
|
|
187
|
+
{"type": "string"},
|
|
188
|
+
{"type": "array", "items": {"type": "string"}, "minItems": 1}
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
"propertyNames": {"pattern": "^[a-z][a-z0-9_]*$"},
|
|
192
|
+
"description": "Named folders a rule refers to with `in:`. A rule never writes a path. The value is a default; the project settings file overrides it for one repository without touching a rule. A value may reference another folder in braces, because a module's own config lives inside whatever custom_module resolves to."
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
"identity": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"additionalProperties": true,
|
|
198
|
+
"description": "How this language composes a qualified name, folds case, and what it must never normalise. Shape not yet derived from enough packs to close.",
|
|
199
|
+
"properties": {
|
|
200
|
+
"call_arguments": {"type": "string", "description": "The tree field of a call that holds its arguments, read by an id or a reference end from {argument: N}. Default: arguments."},
|
|
201
|
+
"call_argument": {"type": "string", "description": "The tree node type that holds one argument of a call; its value is its last named child. Without it, each named child of the arguments field is one argument, a comment or other extra node left out."},
|
|
202
|
+
"call_argument_name": {"type": "string", "description": "The field of an argument that holds its name. A named argument is found by its name, not its position, and its value is its last named child."},
|
|
203
|
+
"call_literals": {"type": "array", "items": {"type": "string"}, "description": "The tree node types of a call argument's text literal. Without them, no argument is a text literal."},
|
|
204
|
+
"call_literal_content": {"type": "array", "items": {"type": "string"}, "description": "The tree node types of a text literal's text. A literal with a named child of any other type is not a text literal."},
|
|
205
|
+
"call_literal_delimiters": {"type": "array", "items": {"type": "string"}, "description": "The tree node types of a text literal's named children that are skipped, such as a quote the grammar names."}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
"boundary": {
|
|
210
|
+
"type": "object",
|
|
211
|
+
"additionalProperties": {
|
|
212
|
+
"type": "object",
|
|
213
|
+
"additionalProperties": false,
|
|
214
|
+
"required": ["type", "ancestry", "names"],
|
|
215
|
+
"properties": {
|
|
216
|
+
"type": {"type": "string", "minLength": 1, "description": "A node type this pack or a pack it depends on declares. The group's names live in its id namespace."},
|
|
217
|
+
"ancestry": {"type": "array", "items": {"type": "string", "minLength": 1}, "minItems": 1, "description": "The edge kinds whose edges carry descent toward a listed name."},
|
|
218
|
+
"names": {
|
|
219
|
+
"type": "object",
|
|
220
|
+
"additionalProperties": {"type": ["string", "null"]},
|
|
221
|
+
"description": "Each qualified name where the framework begins, with the node type that classifies a first-party descendant of it, any node type this pack or a pack it depends on declares, or null for none."
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"description": "Named groups of the names where a framework begins. An edge end no rule found that lands on a listed name becomes a declared node this pack drew, with no location; the engine never reads that code. A boundary node enters the map because first-party code refers to it, never because a pack knows about it, so a listed name no edge reaches is not in the map. Every loaded pack's groups apply, and an id two packs list refuses the map."
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
"tables": {
|
|
229
|
+
"type": "object",
|
|
230
|
+
"additionalProperties": {
|
|
231
|
+
"type": "object",
|
|
232
|
+
"additionalProperties": {"type": "string", "minLength": 1}
|
|
233
|
+
},
|
|
234
|
+
"description": "Name tables by table name, each mapping a written name to a node name. A rule's lookup_last_segment_in step names a table this pack or a pack it depends on declares; this pack's table wins over a dependency's of the same name."
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
"$defs": {
|
|
239
|
+
"exactVersion": {
|
|
240
|
+
"type": "string",
|
|
241
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
242
|
+
"description": "Exact, never a range. Grammar node type names carry no stability guarantee across a minor release, so a range lets a rule that matched yesterday match nothing today, silently."
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://periplus.dev/schema/0/project-settings.schema.json",
|
|
4
|
+
"title": "Periplus project settings",
|
|
5
|
+
"description": "periplus.settings.yml, at the root of the repository being mapped. Committed and reviewed like source. It is the only place a person edits anything the map depends on, and it holds what no pack can know. A general settings file holds what is true for a machine or a user; this one wins where they disagree.",
|
|
6
|
+
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": ["periplus_version"],
|
|
10
|
+
"properties": {
|
|
11
|
+
|
|
12
|
+
"periplus_version": {"type": "integer", "minimum": 0},
|
|
13
|
+
|
|
14
|
+
"packs": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"minItems": 1,
|
|
17
|
+
"items": {"type": "string", "pattern": "^[a-z][a-z0-9_]*@\\d+\\.\\d+\\.\\d+$"},
|
|
18
|
+
"description": "Named, not implied by what happens to be on disk. A pack shipped inside a downloaded module loads because this list names it, not because it is present — which is also the whole of the trust story for third-party packs. Not required: a project nobody has configured has no `packs` key at all, which is the file `periplus init` writes and the state `periplus status` exits 0 on. `minItems` is what keeps the other case refused — a key present and empty is a claim that no pack applies, which is a different statement and one no run should accept quietly."
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
"pack_paths": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"items": {"type": "string"},
|
|
24
|
+
"description": "Extra directories to resolve packs from, beside the ones the tool ships. A pack a person writes for their own repository is found the same way a bundled one is, or third-party packs are second-class by construction."
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"folders": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"additionalProperties": {
|
|
30
|
+
"oneOf": [
|
|
31
|
+
{"type": "string"},
|
|
32
|
+
{"type": "array", "items": {"type": "string"}, "minItems": 1}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"propertyNames": {"pattern": "^[a-z][a-z0-9_]*$"},
|
|
36
|
+
"description": "Overrides the pack's defaults for this repository. Exported configuration is the case that proves it: three real Drupal sites keep it in three different places, one of them computed in PHP at runtime."
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
"first_party": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"properties": {
|
|
43
|
+
"include": {"type": "array", "items": {"type": "string"}},
|
|
44
|
+
"exclude": {"type": "array", "items": {"type": "string"}}
|
|
45
|
+
},
|
|
46
|
+
"description": "Path-based, needing no pack, so a dependency published tomorrow still stops at the boundary. This is also where a dependency is deliberately promoted to first-party, which is the boundary mechanism under test: one line, no engine change."
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
"extensions": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": {"type": "string"},
|
|
52
|
+
"description": "The last word over any pack. An extension can have no correct global meaning — .inc is PHP here and Pascal somewhere else — so the only place that settles is the project."
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
"values": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"additionalProperties": {"type": "string"},
|
|
58
|
+
"propertyNames": {"pattern": "^[a-z][a-z0-9_]*$"},
|
|
59
|
+
"description": "Named text values that no pack can know, such as the module path a project's packages sit under. Every template of every pack may name one as {name}, as it names a path value: a rule's id template, and the template of a file type's path value, so a path value can read {module}/{dir}. A name a loaded pack also gives as a path value, or that a rule captures, is refused when the map is made, and so is a path value template that names a value neither its pattern nor these values give."
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
"exclude": {"type": "array", "items": {"type": "string"}},
|
|
63
|
+
"re_include": {
|
|
64
|
+
"type": "array", "items": {"type": "string"},
|
|
65
|
+
"description": "Takes back a pack's exclusion without forking the pack. It has to exist for the same reason the extension override does: a pack states a convention, and one repository is always the exception."
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
"accepted_unknowns": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"items": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"required": ["id", "rule", "reason", "accepted_by", "accepted_on"],
|
|
74
|
+
"properties": {
|
|
75
|
+
"id": {"type": "string"},
|
|
76
|
+
"rule": {"type": "string", "description": "pack.rule_name, matching the `rule:` key of the rule that failed."},
|
|
77
|
+
"reason": {"type": "string"},
|
|
78
|
+
"accepted_by": {"type": "string"},
|
|
79
|
+
"accepted_on": {"type": "string", "format": "date"}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"description": "Gaps accepted as permanent. Dynamic dispatch, a computed string, an id assembled at runtime. This cannot live in the map, which is generated and must never be hand-edited. Same shape as a lint suppression, and it is what lets an unresolved count reach zero and mean something."
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# The engine
|
|
2
|
+
|
|
3
|
+
What the tool must do, in order, and what it must refuse. The four schemas in `schema/` are
|
|
4
|
+
the machine-checkable half; this is the half a person builds against.
|
|
5
|
+
|
|
6
|
+
Every requirement here comes from something that happened. Where a line exists because
|
|
7
|
+
something broke, the measurement is beside it.
|
|
8
|
+
|
|
9
|
+
## What the engine is
|
|
10
|
+
|
|
11
|
+
A program that reads packs and a settings file, opens source files, and writes a map.
|
|
12
|
+
|
|
13
|
+
It contains no knowledge of any language, framework, or file format. Every construct name,
|
|
14
|
+
condition, id part and emit target comes from a pack. The test is mechanical: the string
|
|
15
|
+
`php`, `go`, `drupal`, `twig` must not appear in the engine outside a comment.
|
|
16
|
+
|
|
17
|
+
It is not a server, a daemon, a database, or a language server. It runs, writes a file, and
|
|
18
|
+
exits.
|
|
19
|
+
|
|
20
|
+
## The run, in order
|
|
21
|
+
|
|
22
|
+
The order is not an implementation detail. Two steps of it are load-bearing and named as
|
|
23
|
+
such below.
|
|
24
|
+
|
|
25
|
+
### 1. Resolve settings
|
|
26
|
+
|
|
27
|
+
Read the project settings file. It names the packs, and it may override any folder a pack
|
|
28
|
+
declares. Absent settings is an error, not a default — the engine must not guess where a
|
|
29
|
+
repository keeps its configuration.
|
|
30
|
+
|
|
31
|
+
### 2. Resolve the pack graph, and build the type table
|
|
32
|
+
|
|
33
|
+
**Before any source file is opened.** A pack may only name a type it declares or that a pack
|
|
34
|
+
in its dependency graph declares; that check runs here, against zero source files, and a
|
|
35
|
+
violation fails the run.
|
|
36
|
+
|
|
37
|
+
Packs form a directed acyclic graph, not a chain. Contributions merge additively and without
|
|
38
|
+
depending on order, because a graph has no single load order to appeal to. Two files
|
|
39
|
+
declaring the same type is a conflict and fails. Two files contributing rules to one type is
|
|
40
|
+
ordinary.
|
|
41
|
+
|
|
42
|
+
There are no kinds of pack. A pack with nothing beneath it is a root and need not be a
|
|
43
|
+
language. If any level were special, a third party could not occupy it.
|
|
44
|
+
|
|
45
|
+
### 3. Select files
|
|
46
|
+
|
|
47
|
+
From the folders each rule names, filtered by the extensions packs claim, minus every
|
|
48
|
+
exclusion.
|
|
49
|
+
|
|
50
|
+
Exclusion is not the boundary. A boundary file is unread and the things inside it may still
|
|
51
|
+
be nodes, because first-party code names them. An excluded file does not exist as far as the
|
|
52
|
+
map is concerned, not even as the target of a reference, and the check happens here, before
|
|
53
|
+
anything parses. A settings file may exclude what a pack would read and re-include what a
|
|
54
|
+
pack excluded; the project has the last word in both directions.
|
|
55
|
+
|
|
56
|
+
### 4. Run source rules
|
|
57
|
+
|
|
58
|
+
Every rule whose `reads` is `file`, over every selected file, in any order. They cannot see
|
|
59
|
+
each other, which is why their order does not matter.
|
|
60
|
+
|
|
61
|
+
A file is opened as a parse tree when a pack claims its extension and declares a grammar,
|
|
62
|
+
and as text otherwise. A pack with no grammar is legal and complete — `packs/twig` maps
|
|
63
|
+
templates with patterns alone.
|
|
64
|
+
|
|
65
|
+
### 5. Run map rules to a fixpoint
|
|
66
|
+
|
|
67
|
+
Every rule whose `reads` is `map`, repeatedly, until nothing changes.
|
|
68
|
+
|
|
69
|
+
It terminates because a pass may only add attributes or refine a type downward through a
|
|
70
|
+
hierarchy the packs declare, and that hierarchy is finite. Nothing moves back up. Edges a map
|
|
71
|
+
rule adds terminate too: map rules match and follow only nodes and edges the rules found or file
|
|
72
|
+
rules emitted, never an end a map rule composed, so the ids a pass reads are fixed once the file
|
|
73
|
+
rules have run. Edges merge on `(kind, from, to)` over that finite set, and a pass that adds
|
|
74
|
+
nothing stops the loop.
|
|
75
|
+
|
|
76
|
+
Each pass reads the map as it stood when the pass began, rules in load order and nodes in id
|
|
77
|
+
order, so a rule reads an edge another map rule made in the next pass, and the map stays
|
|
78
|
+
byte-identical across runs.
|
|
79
|
+
|
|
80
|
+
The engine must report how many passes ran and which rules fired in each. The fixpoint hides
|
|
81
|
+
its own sequencing, and when a classification comes out wrong, "it fired in pass three off a
|
|
82
|
+
type assigned in pass two" is the only thing that makes it traceable.
|
|
83
|
+
|
|
84
|
+
### 6. Merge and resolve
|
|
85
|
+
|
|
86
|
+
Nodes merge on their id. Edges merge on `(kind, from, to)`; their locations accumulate and
|
|
87
|
+
`occurrences` is the count. Deduplicating per pair and discarding the rest was measured at
|
|
88
|
+
186 caller functions against 303 call sites, and one pair in a real map had 58.
|
|
89
|
+
|
|
90
|
+
An endpoint naming no node becomes one — `declared` when a pack's `boundary` lists it,
|
|
91
|
+
`referenced` when nothing does. Doing this took dangling edges in one real map from 792 of 884 to
|
|
92
|
+
zero. A boundary group gives a `type`, the `ancestry` edge kinds and its `names`; a listed node
|
|
93
|
+
takes the group's type, no location, and the drawing pack's provenance. A name's value is a node
|
|
94
|
+
type: a mapped class whose ancestry edges reach the name through mapped classes is classified by
|
|
95
|
+
it, or keeps its type with a skipped row when its type and the value lie on no one chain.
|
|
96
|
+
|
|
97
|
+
### 7. Emit
|
|
98
|
+
|
|
99
|
+
Canonical JSON. Keys in a fixed order, arrays sorted by a declared key, LF, no trailing
|
|
100
|
+
whitespace, no timestamps, no absolute paths.
|
|
101
|
+
|
|
102
|
+
## What it must refuse
|
|
103
|
+
|
|
104
|
+
A run fails, loudly, on any of these. None of them may be a warning.
|
|
105
|
+
|
|
106
|
+
- A pack naming a type nothing in its dependency graph declares.
|
|
107
|
+
- Two packs declaring the same type.
|
|
108
|
+
- An edge whose endpoints are not legal for its kind.
|
|
109
|
+
- An unknown key anywhere in a pack. Three separate silent failures in one day came from
|
|
110
|
+
keys that read fine and did nothing: `on:` parsed as the boolean `true` in five rules; two
|
|
111
|
+
`id:` keys in one mapping discarded 48 rule names; a duplicate `pack:` key threw away a
|
|
112
|
+
whole file.
|
|
113
|
+
- A rule shape the engine cannot execute. The spike printed these and ran on, which is the
|
|
114
|
+
minimum; refusing is better, because a map missing a third of its rules is worse than no
|
|
115
|
+
map.
|
|
116
|
+
- A grammar version that does not match what the pack pinned.
|
|
117
|
+
|
|
118
|
+
## What it must report
|
|
119
|
+
|
|
120
|
+
- Which rules fired, and how many times each.
|
|
121
|
+
- How many passes the fixpoint took.
|
|
122
|
+
- Every rule that matched nothing. A rule that fires zero times is either dead or broken,
|
|
123
|
+
and the difference matters.
|
|
124
|
+
- Every unresolved node, with what the rule looked for and what it found. "Unresolved" alone
|
|
125
|
+
is a shrug. The entry has to read as "add an anchor for this symbol", not "unknown".
|
|
126
|
+
|
|
127
|
+
## What it must never do
|
|
128
|
+
|
|
129
|
+
**Guess.** Where a rule cannot resolve something, it emits an unresolved node recording the
|
|
130
|
+
attempt. It never emits an edge to a name it could not resolve. The spike did, and produced
|
|
131
|
+
221 call edges to bare words — `Draft -> t`, where `t` is a translation method.
|
|
132
|
+
|
|
133
|
+
**Write prose.** Nothing a model wrote enters the map. The determinism gate extracts twice
|
|
134
|
+
and byte-compares; a model writing during extraction fails on the first run. Descriptions are
|
|
135
|
+
a separate committed artifact keyed by node id, joined through each node's `source_hash`.
|
|
136
|
+
|
|
137
|
+
**Carry advice.** A pack may recommend; the map holds the count and the pack holds the note,
|
|
138
|
+
joined at read time.
|
|
139
|
+
|
|
140
|
+
**Depend on locale, working directory, or filesystem order.** Proven by running twice and
|
|
141
|
+
byte-comparing, including under a Turkish locale, where `I` does not lowercase to `i`.
|
|
142
|
+
|
|
143
|
+
## Open, and deliberately not decided here
|
|
144
|
+
|
|
145
|
+
- **Cardinality.** Nothing expresses that a class extends one class and implements many. A
|
|
146
|
+
second `inherits` edge on one PHP class would sit in a map looking fine. It belongs on the
|
|
147
|
+
edge kind, per pack, because Go has no `inherits` at all and a Go struct embeds many.
|
|
148
|
+
- **Descriptions.** The join exists; the artifact has no schema.
|
|
149
|
+
- **Incremental runs.** Every run is a full run. `source_hash` is the obvious lever.
|
|
150
|
+
- **Addressing inside a data file.** `each: "services.*"` is one underspecified line, and two
|
|
151
|
+
packs need it.
|